bit-office 1.2.0 → 1.2.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
@@ -4391,7 +4391,8 @@ var ProjectListEvent = external_exports.object({
4391
4391
  endedAt: external_exports.number(),
4392
4392
  agentNames: external_exports.array(external_exports.string()),
4393
4393
  eventCount: external_exports.number(),
4394
- preview: ProjectPreviewSchema
4394
+ preview: ProjectPreviewSchema,
4395
+ tokenUsage: external_exports.object({ inputTokens: external_exports.number(), outputTokens: external_exports.number() }).optional()
4395
4396
  }))
4396
4397
  });
4397
4398
  var ProjectDataEvent = external_exports.object({
@@ -5335,14 +5336,14 @@ var CONFIG = {
5335
5336
  };
5336
5337
 
5337
5338
  // ../../packages/orchestrator/src/agent-session.ts
5338
- import { spawn as spawn2, execSync as execSync3 } from "child_process";
5339
- import path5 from "path";
5340
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync7 } from "fs";
5339
+ import { spawn, execSync as execSync2 } from "child_process";
5340
+ import path4 from "path";
5341
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync6 } from "fs";
5341
5342
  import { homedir as homedir4 } from "os";
5342
5343
 
5343
5344
  // ../../packages/orchestrator/src/preview-resolver.ts
5344
- import { existsSync as existsSync6 } from "fs";
5345
- import path4 from "path";
5345
+ import { existsSync as existsSync5 } from "fs";
5346
+ import path3 from "path";
5346
5347
 
5347
5348
  // ../../packages/orchestrator/src/resolve-path.ts
5348
5349
  import path2 from "path";
@@ -5362,157 +5363,19 @@ function resolveAgentPath(filePath, projectDir, workspace) {
5362
5363
  return void 0;
5363
5364
  }
5364
5365
 
5365
- // ../../packages/orchestrator/src/preview-server.ts
5366
- import { spawn, execSync as execSync2 } from "child_process";
5367
- import { existsSync as existsSync5 } from "fs";
5368
- import path3 from "path";
5369
- var STATIC_PORT = 9100;
5370
- var PreviewServer = class {
5371
- process = null;
5372
- currentDir = null;
5373
- isDetached = false;
5374
- /**
5375
- * Mode 1: Serve a static file directory on a fixed port.
5376
- * Returns the preview URL for the given file.
5377
- */
5378
- serve(filePath) {
5379
- if (!existsSync5(filePath)) {
5380
- console.log(`[PreviewServer] File not found: ${filePath}`);
5381
- return void 0;
5382
- }
5383
- const dir = path3.dirname(filePath);
5384
- const fileName = path3.basename(filePath);
5385
- this.stop();
5386
- try {
5387
- this.process = spawn("npx", ["serve", dir, "-l", String(STATIC_PORT), "--no-clipboard"], {
5388
- stdio: "ignore",
5389
- detached: true
5390
- });
5391
- this.process.unref();
5392
- this.currentDir = dir;
5393
- this.isDetached = true;
5394
- const url = `http://localhost:${STATIC_PORT}/${fileName}`;
5395
- console.log(`[PreviewServer] Serving ${dir} on port ${STATIC_PORT}`);
5396
- return url;
5397
- } catch (e) {
5398
- console.log(`[PreviewServer] Failed to start static serve: ${e}`);
5399
- return void 0;
5400
- }
5401
- }
5402
- /**
5403
- * Mode 2: Run a command (e.g. "python app.py") and use the specified port.
5404
- * The command is expected to start a server on the given port.
5405
- * Returns the preview URL.
5406
- */
5407
- runCommand(cmd, cwd, port) {
5408
- this.stop();
5409
- try {
5410
- this.process = spawn(cmd, {
5411
- shell: true,
5412
- cwd,
5413
- stdio: "ignore",
5414
- detached: true
5415
- });
5416
- this.process.unref();
5417
- this.currentDir = cwd;
5418
- this.isDetached = true;
5419
- const url = `http://localhost:${port}`;
5420
- console.log(`[PreviewServer] Running "${cmd}" in ${cwd}, preview at port ${port}`);
5421
- return url;
5422
- } catch (e) {
5423
- console.log(`[PreviewServer] Failed to run command: ${e}`);
5424
- return void 0;
5425
- }
5426
- }
5427
- /**
5428
- * Mode 3: Launch a desktop/CLI process (no web preview URL).
5429
- * Used for Pygame, Tkinter, Electron, terminal apps, etc.
5430
- * NOT detached — GUI apps need the login session to access WindowServer (macOS).
5431
- */
5432
- launchProcess(cmd, cwd) {
5433
- this.stop();
5434
- try {
5435
- this.process = spawn(cmd, {
5436
- shell: true,
5437
- cwd,
5438
- stdio: ["ignore", "ignore", "pipe"]
5439
- });
5440
- this.currentDir = cwd;
5441
- this.isDetached = false;
5442
- console.log(`[PreviewServer] Launched "${cmd}" in ${cwd} (pid=${this.process.pid})`);
5443
- this.process.stderr?.on("data", (data) => {
5444
- const msg = data.toString().trim();
5445
- if (msg) console.log(`[PreviewServer] stderr: ${msg.slice(0, 200)}`);
5446
- });
5447
- this.process.on("exit", (code) => {
5448
- console.log(`[PreviewServer] Process exited with code ${code}`);
5449
- });
5450
- } catch (e) {
5451
- console.log(`[PreviewServer] Failed to launch process: ${e}`);
5452
- }
5453
- }
5454
- /** Kill the current process and any orphan process on the static port */
5455
- stop() {
5456
- if (this.process) {
5457
- try {
5458
- if (this.isDetached && this.process.pid) {
5459
- process.kill(-this.process.pid, "SIGTERM");
5460
- } else {
5461
- this.process.kill("SIGTERM");
5462
- }
5463
- } catch {
5464
- try {
5465
- this.process.kill("SIGTERM");
5466
- } catch {
5467
- }
5468
- }
5469
- this.process = null;
5470
- this.currentDir = null;
5471
- this.isDetached = false;
5472
- console.log(`[PreviewServer] Stopped`);
5473
- }
5474
- this.killPortHolder(STATIC_PORT);
5475
- }
5476
- /** Kill whatever process is listening on the given port (best-effort). */
5477
- killPortHolder(port) {
5478
- try {
5479
- const out = execSync2(`lsof -ti :${port}`, { encoding: "utf-8", timeout: 3e3 }).trim();
5480
- if (out) {
5481
- for (const pid of out.split("\n")) {
5482
- const n = parseInt(pid, 10);
5483
- if (n > 0) {
5484
- try {
5485
- process.kill(n, "SIGKILL");
5486
- } catch {
5487
- }
5488
- }
5489
- }
5490
- console.log(`[PreviewServer] Killed orphan process(es) on port ${port}: ${out.replace(/\n/g, ", ")}`);
5491
- }
5492
- } catch {
5493
- }
5494
- }
5495
- };
5496
- var previewServer = new PreviewServer();
5497
-
5498
5366
  // ../../packages/orchestrator/src/preview-resolver.ts
5499
5367
  var EMPTY = { previewUrl: void 0, previewPath: void 0 };
5500
5368
  function resolvePreview(input) {
5501
5369
  const { cwd, workspace } = input;
5502
5370
  if (input.previewCmd && input.previewPort) {
5503
- const url = previewServer.runCommand(input.previewCmd, cwd, input.previewPort);
5504
- if (url) return { previewUrl: url, previewPath: void 0 };
5371
+ return EMPTY;
5505
5372
  }
5506
5373
  if (input.previewCmd && !input.previewPort) {
5507
- console.log(`[PreviewResolver] Desktop app ready (user can Launch): ${input.previewCmd}`);
5508
5374
  return EMPTY;
5509
5375
  }
5510
5376
  if (input.entryFile && /\.html?$/i.test(input.entryFile)) {
5511
5377
  const absPath = resolveAgentPath(input.entryFile, cwd, workspace);
5512
- if (absPath) {
5513
- const url = previewServer.serve(absPath);
5514
- if (url) return { previewUrl: url, previewPath: absPath };
5515
- }
5378
+ if (absPath) return { previewUrl: void 0, previewPath: absPath };
5516
5379
  }
5517
5380
  if (input.stdout) {
5518
5381
  const match = input.stdout.match(/PREVIEW:\s*(https?:\/\/[^\s*)\]>]+)/i);
@@ -5524,28 +5387,19 @@ function resolvePreview(input) {
5524
5387
  const fileMatch = input.stdout.match(/(?:open\s+)?((?:\/[\w./_-]+|[\w./_-]+)\.html?)\b/i);
5525
5388
  if (fileMatch) {
5526
5389
  const absPath = resolveAgentPath(fileMatch[1], cwd, workspace);
5527
- if (absPath) {
5528
- const url = previewServer.serve(absPath);
5529
- if (url) return { previewUrl: url, previewPath: absPath };
5530
- }
5390
+ if (absPath) return { previewUrl: void 0, previewPath: absPath };
5531
5391
  }
5532
5392
  }
5533
5393
  if (input.changedFiles) {
5534
5394
  for (const f of input.changedFiles) {
5535
5395
  if (!/\.html?$/i.test(f)) continue;
5536
5396
  const absPath = resolveAgentPath(f, cwd, workspace);
5537
- if (absPath) {
5538
- const url = previewServer.serve(absPath);
5539
- if (url) return { previewUrl: url, previewPath: absPath };
5540
- }
5397
+ if (absPath) return { previewUrl: void 0, previewPath: absPath };
5541
5398
  }
5542
5399
  }
5543
5400
  for (const candidate of CONFIG.preview.buildOutputCandidates) {
5544
- const absPath = path4.join(cwd, candidate);
5545
- if (existsSync6(absPath)) {
5546
- const url = previewServer.serve(absPath);
5547
- if (url) return { previewUrl: url, previewPath: absPath };
5548
- }
5401
+ const absPath = path3.join(cwd, candidate);
5402
+ if (existsSync5(absPath)) return { previewUrl: void 0, previewPath: absPath };
5549
5403
  }
5550
5404
  return EMPTY;
5551
5405
  }
@@ -5611,10 +5465,10 @@ function extractFallbackSummary(raw, _hasFiles, _entryFile, _projectDir) {
5611
5465
 
5612
5466
  // ../../packages/orchestrator/src/agent-session.ts
5613
5467
  import { nanoid as nanoid3 } from "nanoid";
5614
- var SESSION_FILE = path5.join(homedir4(), ".bit-office", "agent-sessions.json");
5468
+ var SESSION_FILE = path4.join(homedir4(), ".bit-office", "agent-sessions.json");
5615
5469
  function loadSessionMap() {
5616
5470
  try {
5617
- if (existsSync7(SESSION_FILE)) return JSON.parse(readFileSync4(SESSION_FILE, "utf-8"));
5471
+ if (existsSync6(SESSION_FILE)) return JSON.parse(readFileSync4(SESSION_FILE, "utf-8"));
5618
5472
  } catch {
5619
5473
  }
5620
5474
  return {};
@@ -5623,8 +5477,8 @@ function clearSessionId(agentId) {
5623
5477
  saveSessionId(agentId, null);
5624
5478
  }
5625
5479
  function saveSessionId(agentId, sessionId) {
5626
- const dir = path5.dirname(SESSION_FILE);
5627
- if (!existsSync7(dir)) mkdirSync4(dir, { recursive: true });
5480
+ const dir = path4.dirname(SESSION_FILE);
5481
+ if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
5628
5482
  const map = loadSessionMap();
5629
5483
  if (sessionId) {
5630
5484
  map[agentId] = sessionId;
@@ -5656,6 +5510,8 @@ var AgentSession = class {
5656
5510
  stderrBuffer = "";
5657
5511
  taskInputTokens = 0;
5658
5512
  taskOutputTokens = 0;
5513
+ /** Dedup same-turn repeated usage in assistant messages */
5514
+ lastUsageSignature = "";
5659
5515
  hasHistory;
5660
5516
  sessionId;
5661
5517
  taskQueue = [];
@@ -5761,6 +5617,7 @@ var AgentSession = class {
5761
5617
  this.stderrBuffer = "";
5762
5618
  this.taskInputTokens = 0;
5763
5619
  this.taskOutputTokens = 0;
5620
+ this.lastUsageSignature = "";
5764
5621
  this.onEvent({
5765
5622
  type: "task:started",
5766
5623
  agentId: this.agentId,
@@ -5812,12 +5669,12 @@ var AgentSession = class {
5812
5669
  skipResume: isFirstExecute && this.hasHistory
5813
5670
  });
5814
5671
  try {
5815
- const whichPath = execSync3(`which ${this.backend.command}`, { env: cleanEnv, encoding: "utf-8", timeout: 3e3 }).trim();
5672
+ const whichPath = execSync2(`which ${this.backend.command}`, { env: cleanEnv, encoding: "utf-8", timeout: 3e3 }).trim();
5816
5673
  console.log(`[Agent ${this.name}] Binary: ${whichPath}, CLAUDECODE=${cleanEnv.CLAUDECODE ?? "unset"}, ENTRYPOINT=${cleanEnv.CLAUDE_CODE_ENTRYPOINT ?? "unset"}`);
5817
5674
  } catch {
5818
5675
  }
5819
5676
  console.log(`[Agent ${this.name}] Spawning: ${this.backend.command} ${args.map((a) => a.length > 80 ? a.slice(0, 80) + "..." : a).join(" ")}`);
5820
- this.process = spawn2(this.backend.command, args, {
5677
+ this.process = spawn(this.backend.command, args, {
5821
5678
  cwd,
5822
5679
  env: cleanEnv,
5823
5680
  stdio: ["ignore", "pipe", "pipe"],
@@ -5910,8 +5767,20 @@ var AgentSession = class {
5910
5767
  if (msg.type === "assistant" && msg.message?.content) {
5911
5768
  if (msg.message.usage) {
5912
5769
  const usage = msg.message.usage;
5913
- if (typeof usage.input_tokens === "number") this.taskInputTokens += usage.input_tokens;
5914
- if (typeof usage.output_tokens === "number") this.taskOutputTokens += usage.output_tokens;
5770
+ const turnIn = (usage.input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0);
5771
+ const turnOut = usage.output_tokens ?? 0;
5772
+ const sig = `${turnIn}:${turnOut}`;
5773
+ if (sig !== this.lastUsageSignature) {
5774
+ this.lastUsageSignature = sig;
5775
+ this.taskInputTokens += turnIn;
5776
+ this.taskOutputTokens += turnOut;
5777
+ this.onEvent({
5778
+ type: "token:update",
5779
+ agentId: this.agentId,
5780
+ inputTokens: this.taskInputTokens,
5781
+ outputTokens: this.taskOutputTokens
5782
+ });
5783
+ }
5915
5784
  }
5916
5785
  for (const block of msg.message.content) {
5917
5786
  if (block.type === "text" && block.text) {
@@ -5922,12 +5791,27 @@ var AgentSession = class {
5922
5791
  console.log(`[Agent ${this.name} thinking] ${block.thinking.slice(0, 120)}...`);
5923
5792
  }
5924
5793
  }
5925
- } else if (msg.type === "result" && msg.result) {
5926
- if (!this.stdoutBuffer) {
5927
- this.stdoutBuffer = msg.result;
5928
- handleTextLine(msg.result);
5794
+ } else if (msg.type === "result") {
5795
+ if (msg.usage) {
5796
+ const usage = msg.usage;
5797
+ const totalIn = (usage.input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0);
5798
+ const totalOut = usage.output_tokens ?? 0;
5799
+ this.taskInputTokens = totalIn;
5800
+ this.taskOutputTokens = totalOut;
5801
+ this.onEvent({
5802
+ type: "token:update",
5803
+ agentId: this.agentId,
5804
+ inputTokens: this.taskInputTokens,
5805
+ outputTokens: this.taskOutputTokens
5806
+ });
5807
+ }
5808
+ if (msg.result) {
5809
+ if (!this.stdoutBuffer) {
5810
+ this.stdoutBuffer = msg.result;
5811
+ handleTextLine(msg.result);
5812
+ }
5813
+ this._lastResultText = msg.result;
5929
5814
  }
5930
- this._lastResultText = msg.result;
5931
5815
  }
5932
5816
  continue;
5933
5817
  } catch {
@@ -5948,11 +5832,18 @@ var AgentSession = class {
5948
5832
  }
5949
5833
  });
5950
5834
  this.process.on("close", (code) => {
5835
+ const agentPid = this.process?.pid;
5951
5836
  this.process = null;
5952
5837
  if (this.taskTimeout) {
5953
5838
  clearTimeout(this.taskTimeout);
5954
5839
  this.taskTimeout = null;
5955
5840
  }
5841
+ if (agentPid) {
5842
+ try {
5843
+ process.kill(-agentPid, "SIGTERM");
5844
+ } catch {
5845
+ }
5846
+ }
5956
5847
  const remaining = jsonLineBuf.trim();
5957
5848
  if (remaining) {
5958
5849
  jsonLineBuf = "";
@@ -6078,7 +5969,7 @@ var AgentSession = class {
6078
5969
  detectPreview() {
6079
5970
  const result = this.extractResult();
6080
5971
  const baseCwd = this.currentCwd ?? this.workspace;
6081
- const cwd = result.projectDir ? path5.isAbsolute(result.projectDir) ? result.projectDir : path5.join(baseCwd, result.projectDir) : baseCwd;
5972
+ const cwd = result.projectDir ? path4.isAbsolute(result.projectDir) ? result.projectDir : path4.join(baseCwd, result.projectDir) : baseCwd;
6082
5973
  return resolvePreview({
6083
5974
  entryFile: result.entryFile,
6084
5975
  previewCmd: result.previewCmd,
@@ -6293,7 +6184,7 @@ var AgentManager = class {
6293
6184
 
6294
6185
  // ../../packages/orchestrator/src/delegation.ts
6295
6186
  import { nanoid as nanoid4 } from "nanoid";
6296
- import path6 from "path";
6187
+ import path5 from "path";
6297
6188
  var DelegationRouter = class {
6298
6189
  /** All per-task delegation metadata, keyed by taskId */
6299
6190
  tasks = /* @__PURE__ */ new Map();
@@ -6480,7 +6371,7 @@ var DelegationRouter = class {
6480
6371
  const dirPart = dirMatch[1].replace(/\/$/, "");
6481
6372
  const leaderSession = this.agentManager.get(fromAgentId);
6482
6373
  if (leaderSession) {
6483
- repoPath = path6.resolve(leaderSession.workspaceDir, dirPart);
6374
+ repoPath = path5.resolve(leaderSession.workspaceDir, dirPart);
6484
6375
  }
6485
6376
  }
6486
6377
  }
@@ -6767,8 +6658,8 @@ ${resultLines2}`,
6767
6658
  };
6768
6659
 
6769
6660
  // ../../packages/orchestrator/src/prompt-templates.ts
6770
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync8 } from "fs";
6771
- import path7 from "path";
6661
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync7 } from "fs";
6662
+ import path6 from "path";
6772
6663
  var PROMPT_DEFAULTS = {
6773
6664
  "leader-initial": `You are {{name}}, the Team Lead. {{personality}}
6774
6665
  You CANNOT write code, run commands, or use any tools. You can ONLY delegate.
@@ -6862,7 +6753,7 @@ Check WHO sent this result, then follow the matching branch:
6862
6753
 
6863
6754
  ENTRY_FILE: <from dev \u2014 e.g. index.html, dist/index.html. OMIT if dev didn't provide one>
6864
6755
  PREVIEW_CMD: <from dev \u2014 e.g. "python app.py". OMIT if dev didn't provide one. NEVER use "npm run dev" or "npm start"!>
6865
- PREVIEW_PORT: <from dev \u2014 e.g. 5000, 3000. OMIT if dev didn't provide one>
6756
+ PREVIEW_PORT: <from dev \u2014 e.g. 5000. OMIT if dev didn't provide one>
6866
6757
  SUMMARY: <2-3 sentence description of what was built>
6867
6758
 
6868
6759
  RULES:
@@ -6873,95 +6764,65 @@ RULES:
6873
6764
  - Do NOT include PROJECT_DIR \u2014 the system manages project directories automatically.`,
6874
6765
  "worker-initial": `Your name is {{name}}, your role is {{role}}. {{personality}}
6875
6766
 
6876
- CONVERGENCE RULES (follow strictly):
6877
- - Do the MINIMUM needed to satisfy the task. Simple and working beats perfect and slow.
6878
- - Only touch files directly required by this task. Do NOT refactor, clean up, or "improve" unrelated code.
6879
- - If you are uncertain between two approaches, choose the simpler one and note it in SUMMARY.
6880
- - Do NOT add features, error handling, or improvements that were not explicitly asked for.
6881
-
6882
- HARD LIMITS:
6883
- - NEVER run "npm run dev", "npm start", "npx vite", "python -m http.server", or ANY command that starts a long-running server. These will hang forever and waste your budget. The system handles preview serving automatically.
6884
- - Do NOT create backend servers, WebSocket servers, or any server-side code UNLESS the task explicitly requires one. Default to static HTML/CSS/JS.
6885
- - You MAY install dependencies (npm install, pip install) and run ONE-SHOT build commands (npm run build, npx tsc). Never run watch/serve/dev commands.
6767
+ RULES:
6768
+ - Do the MINIMUM needed. Simple and working beats perfect.
6769
+ - NEVER run long-running commands (npm run dev, npm start, npx vite, live-server, python -m http.server). They hang forever and you will be killed. The system serves previews automatically.
6770
+ - Do NOT launch GUI apps (Pygame, Tkinter, Electron) or dev servers. You CANNOT see UI.
6771
+ - You MAY run one-shot commands: npm install, npm run build, npx tsc, syntax checks.
6772
+ - Default to static HTML/CSS/JS unless a backend is explicitly required.
6886
6773
  {{soloHint}}
6887
6774
  {{memory}}
6888
- Start with one sentence describing your approach. Then do the work.
6889
-
6890
- You are responsible for the COMPLETE deliverable \u2014 not just source files. This means:
6891
- 1. Project setup: create all config files needed (package.json, tsconfig, build config, requirements.txt, etc.)
6892
- 2. All source code
6893
- 3. Build & verify: if the project has a build step, RUN IT and fix errors until it passes
6894
- 4. Report how to run/preview the result (see deliverable types below)
6895
-
6896
- VERIFICATION (MANDATORY before reporting STATUS: done):
6897
- - If you created a package.json with a build script \u2192 run "npm run build" (ONE-SHOT), fix errors until it succeeds, confirm the output file exists. NEVER run "npm run dev" or "npm start" \u2014 these hang forever.
6898
- - If your deliverable is an HTML file \u2192 confirm it exists and references valid scripts/styles
6899
- - If your deliverable is a script (Python, Node, etc.) \u2192 run a syntax check (python -c "import ast; ast.parse(open('app.py').read())" or node --check app.js)
6900
- - If NONE of the above apply \u2192 at minimum list the files and confirm the entry point exists
6901
- - IMPORTANT: Do NOT launch GUI/desktop applications (Pygame, Tkinter, Electron, etc.) \u2014 they open windows that cannot be controlled. Do NOT start dev servers (vite, webpack-dev-server, live-server) \u2014 they never exit.
6902
- - FINAL CHECK: confirm you can fill in at least ENTRY_FILE or PREVIEW_CMD (see deliverable types). If you cannot, your deliverable is incomplete \u2014 fix it before reporting.
6903
- - Do NOT report STATUS: done unless verification passes. Fix problems yourself first.
6904
- - STATUS: failed is ONLY for truly unsolvable problems (missing API keys, no network, system-level issues).
6905
-
6906
- ===== DELIVERABLE TYPES =====
6907
- ALWAYS prefer type A (static web) unless the task EXPLICITLY requires a server or desktop app.
6908
- Games, interactive demos, visualizations, and web pages should ALL be static HTML.
6909
6775
 
6910
- A) STATIC WEB (HTML/CSS/JS \u2014 no server needed) \u2014 DEFAULT CHOICE:
6911
- ENTRY_FILE: index.html (the HTML file to open \u2014 e.g. index.html, dist/index.html, build/index.html)
6912
- This is the preferred approach. Put everything in a single HTML file or a small set of static files.
6776
+ OUTPUT STYLE:
6777
+ - While working, output a SHORT status line (\u22648 words) at each major step, prefixed with \u2192. Example: "\u2192 Setting up project" or "\u2192 Building game logic". No other prose or narration. Do NOT write "Let me...", "I'll now...", "Looking at..." \u2014 just do the work.
6778
+ - After all work is done, output ONLY the structured result block below.
6913
6779
 
6914
- B) WEB SERVER \u2014 ONLY if the task explicitly requires a backend (database, API proxy, user auth, etc.):
6915
- PREVIEW_CMD: python app.py (the command to start the server)
6916
- PREVIEW_PORT: 5000 (the port the server listens on \u2014 REQUIRED for web servers)
6780
+ DELIVERABLE:
6781
+ - You own the COMPLETE deliverable: project setup, all source code, build & verify.
6782
+ - STATUS: failed is ONLY for truly unsolvable problems (missing API keys, system issues).
6917
6783
 
6918
- C) DESKTOP/CLI APP (Pygame, Tkinter, Electron, JavaFX, terminal tool, native GUI, etc.):
6919
- PREVIEW_CMD: python game.py (the command to launch the app \u2014 NO PREVIEW_PORT needed)
6784
+ VERIFY BEFORE REPORTING DONE (mandatory):
6785
+ - If package.json has a build script \u2192 run "npm run build" (one-shot), fix errors until it passes.
6786
+ - If HTML deliverable \u2192 confirm the file exists and references valid scripts/styles.
6787
+ - If script (Python/Node) \u2192 run syntax check (node --check / python -c "import ast; ...").
6788
+ - FINAL CHECK: you MUST be able to fill in ENTRY_FILE or PREVIEW_CMD below. If not, your deliverable is incomplete \u2014 fix it first.
6920
6789
 
6921
- OUTPUT:
6790
+ DELIVERABLE TYPES (prefer A):
6791
+ A) STATIC WEB \u2192 ENTRY_FILE: index.html
6792
+ B) WEB SERVER (only if backend needed) \u2192 PREVIEW_CMD + PREVIEW_PORT
6793
+ C) DESKTOP/CLI \u2192 PREVIEW_CMD only
6922
6794
 
6795
+ RESULT FORMAT:
6923
6796
  STATUS: done | failed
6924
- FILES_CHANGED: (list all files created or modified, one per line)
6925
- ENTRY_FILE: (type A only \u2014 path to the HTML file)
6926
- PREVIEW_CMD: (types B and C ONLY \u2014 OMIT this field entirely for static web projects)
6927
- PREVIEW_PORT: (type B only \u2014 the port the server listens on)
6928
- SUMMARY: (one sentence: what you built + how to run/preview it)
6929
-
6930
- You MUST provide at least ENTRY_FILE or PREVIEW_CMD. For games and interactive projects, ENTRY_FILE is almost always correct.
6797
+ FILES_CHANGED: (one per line)
6798
+ ENTRY_FILE: (type A)
6799
+ PREVIEW_CMD: (types B/C only)
6800
+ PREVIEW_PORT: (type B only)
6801
+ SUMMARY: (one sentence)
6931
6802
 
6932
6803
  {{prompt}}`,
6933
6804
  "worker-reviewer-initial": `Your name is {{name}}, your role is {{role}}. {{personality}}
6934
6805
 
6935
- CONVERGENCE RULES (follow strictly):
6936
- - Do the MINIMUM needed to satisfy the task. Simple and working beats perfect and slow.
6937
- - Only touch files directly required by this task. Do NOT refactor, clean up, or "improve" unrelated code.
6938
- - If you are uncertain between two approaches, choose the simpler one and note it in SUMMARY.
6939
- - Do NOT add features, error handling, or improvements that were not explicitly asked for.
6940
-
6941
- HARD LIMITS:
6942
- - NEVER run "npm run dev", "npm start", "npx vite", or ANY long-running server command. These hang forever. Only use one-shot commands like "npm run build" or "node --check".
6943
- - Do NOT launch GUI/desktop applications (Pygame, Tkinter, Electron, etc.) to test them \u2014 they open windows that cannot be controlled. Use syntax checks, import checks, and code reading only.
6944
-
6945
- Code Quality (must check):
6946
- - Correctness: crashes, broken logic, missing files, syntax errors.
6947
- - Verify the deliverable can actually run: check that entry point exists, dependencies are declared, build output is present. For GUI/desktop apps, verify via code review and syntax checks \u2014 do NOT run them.
6948
- - VERIFY WITH TOOLS, not just the developer's summary. Run "ls" to confirm reported files exist. If ENTRY_FILE is claimed, check the file is there and references valid scripts/styles. Do not trust STATUS: done at face value.
6949
- - Do NOT flag security issues in prototypes \u2014 this is a demo, not production code.
6806
+ RULES:
6807
+ - NEVER run servers, dev commands, or GUI apps. You CANNOT see UI.
6808
+ - ONLY use: code reading, "ls" to check files, "npm run build" (one-shot), syntax checks.
6809
+ - This is a prototype \u2014 do NOT nitpick style, naming, formatting, or security.
6950
6810
 
6951
- Feature Completeness (must check):
6952
- - Compare the deliverable against the key features listed in your task assignment.
6953
- - Flag CORE features that are completely missing or non-functional as ISSUES.
6954
- - Do NOT fail for polish, extras, or stretch goals \u2014 this is a prototype. Focus on whether the main functionality works.
6811
+ OUTPUT STYLE:
6812
+ - While reviewing, output a SHORT status line (\u22648 words) at each step, prefixed with \u2192. Example: "\u2192 Checking file structure" or "\u2192 Reading game logic". No other prose.
6813
+ - After review, output ONLY the verdict block below.
6955
6814
 
6956
- Do NOT nitpick style, naming, formatting, or security hardening. This is a prototype, not production code.
6957
- Focus ONLY on: does it run? Does it do what was asked?
6815
+ REVIEW CHECKLIST:
6816
+ 1. VERIFY files exist with "ls" \u2014 do NOT trust the developer's summary at face value. Check ENTRY_FILE is real and references valid scripts/styles.
6817
+ 2. READ the code to verify logic. Check for crashes, broken logic, missing files, syntax errors.
6818
+ 3. Feature completeness: compare against key features in your task. Flag CORE features missing/broken as ISSUES. Ignore polish/extras.
6958
6819
 
6959
6820
  VERDICT: PASS | FAIL
6960
- - PASS = code runs without crashes AND core features are implemented (even if rough)
6961
- - FAIL = crashes/bugs that prevent usage OR core features are missing/broken
6962
- ISSUES: (numbered list \u2014 bugs, security problems, or missing core features)
6963
- SUGGESTIONS: (optional \u2014 minor non-blocking observations, keep brief)
6964
- SUMMARY: (one sentence overall assessment)
6821
+ - PASS = runs without crashes AND core features implemented
6822
+ - FAIL = crashes/bugs prevent usage OR core features missing
6823
+ ISSUES: (numbered list)
6824
+ SUGGESTIONS: (optional, brief)
6825
+ SUMMARY: (one sentence)
6965
6826
 
6966
6827
  {{prompt}}`,
6967
6828
  "worker-continue": `{{prompt}}`,
@@ -7112,12 +6973,12 @@ var PromptEngine = class {
7112
6973
  console.log(`[Prompts] No promptsDir configured, using ${Object.keys(PROMPT_DEFAULTS).length} default templates`);
7113
6974
  return;
7114
6975
  }
7115
- if (!existsSync8(this.promptsDir)) {
6976
+ if (!existsSync7(this.promptsDir)) {
7116
6977
  mkdirSync5(this.promptsDir, { recursive: true });
7117
6978
  }
7118
6979
  let written = 0;
7119
6980
  for (const [name, content] of Object.entries(PROMPT_DEFAULTS)) {
7120
- const filePath = path7.join(this.promptsDir, `${name}.md`);
6981
+ const filePath = path6.join(this.promptsDir, `${name}.md`);
7121
6982
  writeFileSync5(filePath, content, "utf-8");
7122
6983
  written++;
7123
6984
  }
@@ -7133,8 +6994,8 @@ var PromptEngine = class {
7133
6994
  let defaulted = 0;
7134
6995
  if (this.promptsDir) {
7135
6996
  for (const name of Object.keys(PROMPT_DEFAULTS)) {
7136
- const filePath = path7.join(this.promptsDir, `${name}.md`);
7137
- if (existsSync8(filePath)) {
6997
+ const filePath = path6.join(this.promptsDir, `${name}.md`);
6998
+ if (existsSync7(filePath)) {
7138
6999
  try {
7139
7000
  merged[name] = readFileSync5(filePath, "utf-8");
7140
7001
  loaded++;
@@ -7397,7 +7258,7 @@ var PhaseMachine = class {
7397
7258
  };
7398
7259
 
7399
7260
  // ../../packages/orchestrator/src/result-finalizer.ts
7400
- import path8 from "path";
7261
+ import path7 from "path";
7401
7262
  function finalizeTeamResult(ctx) {
7402
7263
  const { result, teamPreview, teamChangedFiles, projectDir, workspace } = ctx;
7403
7264
  if (teamChangedFiles.size > 0) {
@@ -7419,7 +7280,7 @@ function finalizeTeamResult(ctx) {
7419
7280
  }
7420
7281
  validateEntryFile(result, projectDir ?? workspace, workspace);
7421
7282
  autoConstructPreviewCmd(result);
7422
- if (!result.previewUrl) {
7283
+ if (!result.previewUrl && !result.previewPath) {
7423
7284
  resolvePreviewUrlFromTeam(result, ctx);
7424
7285
  }
7425
7286
  }
@@ -7427,12 +7288,12 @@ function validateEntryFile(result, projectDir, workspace) {
7427
7288
  if (!result.entryFile) return;
7428
7289
  const resolved = resolveAgentPath(result.entryFile, projectDir, workspace);
7429
7290
  if (resolved) {
7430
- result.entryFile = path8.relative(projectDir, resolved);
7291
+ result.entryFile = path7.relative(projectDir, resolved);
7431
7292
  return;
7432
7293
  }
7433
7294
  const allFiles = result.changedFiles ?? [];
7434
- const ext = path8.extname(result.entryFile).toLowerCase();
7435
- const candidate = allFiles.map((f) => path8.basename(f)).find((f) => path8.extname(f).toLowerCase() === ext);
7295
+ const ext = path7.extname(result.entryFile).toLowerCase();
7296
+ const candidate = allFiles.map((f) => path7.basename(f)).find((f) => path7.extname(f).toLowerCase() === ext);
7436
7297
  if (candidate) {
7437
7298
  console.log(`[ResultFinalizer] entryFile "${result.entryFile}" not found, using "${candidate}" from changedFiles`);
7438
7299
  result.entryFile = candidate;
@@ -7443,7 +7304,7 @@ function validateEntryFile(result, projectDir, workspace) {
7443
7304
  }
7444
7305
  function autoConstructPreviewCmd(result) {
7445
7306
  if (!result.entryFile || result.previewCmd || /\.html?$/i.test(result.entryFile)) return;
7446
- const ext = path8.extname(result.entryFile).toLowerCase();
7307
+ const ext = path7.extname(result.entryFile).toLowerCase();
7447
7308
  const runner = CONFIG.preview.runners[ext];
7448
7309
  if (runner) {
7449
7310
  result.previewCmd = `${runner} ${result.entryFile}`;
@@ -7454,9 +7315,9 @@ function resolvePreviewUrlFromTeam(result, ctx) {
7454
7315
  const { projectDir, workspace } = ctx;
7455
7316
  const resolveDir = projectDir ?? workspace;
7456
7317
  const workerPreview = ctx.detectWorkerPreview();
7457
- if (workerPreview?.previewUrl) {
7458
- result.previewUrl = workerPreview.previewUrl;
7459
- result.previewPath = workerPreview.previewPath;
7318
+ if (workerPreview?.previewUrl || workerPreview?.previewPath) {
7319
+ if (workerPreview.previewUrl) result.previewUrl = workerPreview.previewUrl;
7320
+ if (workerPreview.previewPath) result.previewPath = workerPreview.previewPath;
7460
7321
  return;
7461
7322
  }
7462
7323
  const allChangedFiles = result.changedFiles ?? [];
@@ -7468,19 +7329,17 @@ function resolvePreviewUrlFromTeam(result, ctx) {
7468
7329
  cwd: resolveDir,
7469
7330
  workspace
7470
7331
  });
7471
- if (preview.previewUrl) {
7472
- result.previewUrl = preview.previewUrl;
7473
- result.previewPath = preview.previewPath;
7474
- }
7332
+ if (preview.previewUrl) result.previewUrl = preview.previewUrl;
7333
+ if (preview.previewPath) result.previewPath = preview.previewPath;
7475
7334
  }
7476
7335
 
7477
7336
  // ../../packages/orchestrator/src/worktree.ts
7478
- import { execSync as execSync4 } from "child_process";
7479
- import path9 from "path";
7337
+ import { execSync as execSync3 } from "child_process";
7338
+ import path8 from "path";
7480
7339
  var TIMEOUT = 5e3;
7481
7340
  function isGitRepo(cwd) {
7482
7341
  try {
7483
- execSync4("git rev-parse --is-inside-work-tree", { cwd, stdio: "ignore", timeout: TIMEOUT });
7342
+ execSync3("git rev-parse --is-inside-work-tree", { cwd, stdio: "ignore", timeout: TIMEOUT });
7484
7343
  return true;
7485
7344
  } catch {
7486
7345
  return false;
@@ -7488,12 +7347,12 @@ function isGitRepo(cwd) {
7488
7347
  }
7489
7348
  function createWorktree(workspace, agentId, taskId, agentName) {
7490
7349
  if (!isGitRepo(workspace)) return null;
7491
- const worktreeDir = path9.join(workspace, ".worktrees");
7350
+ const worktreeDir = path8.join(workspace, ".worktrees");
7492
7351
  const worktreeName = `${agentId}-${taskId}`;
7493
- const worktreePath = path9.join(worktreeDir, worktreeName);
7352
+ const worktreePath = path8.join(worktreeDir, worktreeName);
7494
7353
  const branch = `agent/${agentName.toLowerCase().replace(/\s+/g, "-")}/${taskId}`;
7495
7354
  try {
7496
- execSync4(`git worktree add "${worktreePath}" -b "${branch}"`, {
7355
+ execSync3(`git worktree add "${worktreePath}" -b "${branch}"`, {
7497
7356
  cwd: workspace,
7498
7357
  stdio: "pipe",
7499
7358
  timeout: TIMEOUT
@@ -7506,61 +7365,61 @@ function createWorktree(workspace, agentId, taskId, agentName) {
7506
7365
  }
7507
7366
  function mergeWorktree(workspace, worktreePath, branch) {
7508
7367
  try {
7509
- execSync4(`git merge --no-ff "${branch}"`, {
7368
+ execSync3(`git merge --no-ff "${branch}"`, {
7510
7369
  cwd: workspace,
7511
7370
  stdio: "pipe",
7512
7371
  timeout: TIMEOUT
7513
7372
  });
7514
7373
  try {
7515
- execSync4(`git worktree remove "${worktreePath}"`, { cwd: workspace, stdio: "pipe", timeout: TIMEOUT });
7374
+ execSync3(`git worktree remove "${worktreePath}"`, { cwd: workspace, stdio: "pipe", timeout: TIMEOUT });
7516
7375
  } catch {
7517
7376
  }
7518
7377
  try {
7519
- execSync4(`git branch -d "${branch}"`, { cwd: workspace, stdio: "pipe", timeout: TIMEOUT });
7378
+ execSync3(`git branch -d "${branch}"`, { cwd: workspace, stdio: "pipe", timeout: TIMEOUT });
7520
7379
  } catch {
7521
7380
  }
7522
7381
  return { success: true };
7523
7382
  } catch (err) {
7524
7383
  let conflictFiles = [];
7525
7384
  try {
7526
- const output = execSync4("git diff --name-only --diff-filter=U", {
7385
+ const output = execSync3("git diff --name-only --diff-filter=U", {
7527
7386
  cwd: workspace,
7528
7387
  encoding: "utf-8",
7529
7388
  timeout: TIMEOUT
7530
7389
  }).trim();
7531
7390
  conflictFiles = output ? output.split("\n") : [];
7532
- execSync4("git merge --abort", { cwd: workspace, stdio: "pipe", timeout: TIMEOUT });
7391
+ execSync3("git merge --abort", { cwd: workspace, stdio: "pipe", timeout: TIMEOUT });
7533
7392
  } catch {
7534
7393
  }
7535
7394
  return { success: false, conflictFiles };
7536
7395
  }
7537
7396
  }
7538
7397
  function removeWorktree(worktreePath, branch, workspace) {
7539
- const cwd = workspace ?? path9.dirname(path9.dirname(worktreePath));
7398
+ const cwd = workspace ?? path8.dirname(path8.dirname(worktreePath));
7540
7399
  try {
7541
- execSync4(`git worktree remove --force "${worktreePath}"`, { cwd, stdio: "pipe", timeout: TIMEOUT });
7400
+ execSync3(`git worktree remove --force "${worktreePath}"`, { cwd, stdio: "pipe", timeout: TIMEOUT });
7542
7401
  } catch {
7543
7402
  }
7544
7403
  try {
7545
- execSync4(`git branch -D "${branch}"`, { cwd, stdio: "pipe", timeout: TIMEOUT });
7404
+ execSync3(`git branch -D "${branch}"`, { cwd, stdio: "pipe", timeout: TIMEOUT });
7546
7405
  } catch {
7547
7406
  }
7548
7407
  }
7549
7408
 
7550
7409
  // ../../packages/orchestrator/src/memory.ts
7551
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync9 } from "fs";
7552
- import path10 from "path";
7410
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync8 } from "fs";
7411
+ import path9 from "path";
7553
7412
  import { homedir as homedir5 } from "os";
7554
- var MEMORY_DIR = path10.join(homedir5(), ".bit-office", "memory");
7413
+ var MEMORY_DIR = path9.join(homedir5(), ".bit-office", "memory");
7555
7414
  function ensureDir() {
7556
- if (!existsSync9(MEMORY_DIR)) {
7415
+ if (!existsSync8(MEMORY_DIR)) {
7557
7416
  mkdirSync6(MEMORY_DIR, { recursive: true });
7558
7417
  }
7559
7418
  }
7560
7419
  function loadStore() {
7561
- const filePath = path10.join(MEMORY_DIR, "memory.json");
7420
+ const filePath = path9.join(MEMORY_DIR, "memory.json");
7562
7421
  try {
7563
- if (existsSync9(filePath)) {
7422
+ if (existsSync8(filePath)) {
7564
7423
  return JSON.parse(readFileSync6(filePath, "utf-8"));
7565
7424
  }
7566
7425
  } catch {
@@ -7569,7 +7428,7 @@ function loadStore() {
7569
7428
  }
7570
7429
  function saveStore(store) {
7571
7430
  ensureDir();
7572
- const filePath = path10.join(MEMORY_DIR, "memory.json");
7431
+ const filePath = path9.join(MEMORY_DIR, "memory.json");
7573
7432
  writeFileSync6(filePath, JSON.stringify(store, null, 2), "utf-8");
7574
7433
  }
7575
7434
  function recordReviewFeedback(reviewOutput) {
@@ -8210,6 +8069,149 @@ var Orchestrator = class extends EventEmitter {
8210
8069
  }
8211
8070
  };
8212
8071
 
8072
+ // ../../packages/orchestrator/src/preview-server.ts
8073
+ import { spawn as spawn2, execSync as execSync4 } from "child_process";
8074
+ import { existsSync as existsSync9 } from "fs";
8075
+ import path10 from "path";
8076
+ var STATIC_PORT = 9100;
8077
+ var COMMAND_PORT = 9101;
8078
+ var PreviewServer = class {
8079
+ process = null;
8080
+ currentDir = null;
8081
+ isDetached = false;
8082
+ /**
8083
+ * Mode 1: Serve a static file directory on a fixed port.
8084
+ * Returns the preview URL for the given file.
8085
+ */
8086
+ serve(filePath) {
8087
+ if (!existsSync9(filePath)) {
8088
+ console.log(`[PreviewServer] File not found: ${filePath}`);
8089
+ return void 0;
8090
+ }
8091
+ const dir = path10.dirname(filePath);
8092
+ const fileName = path10.basename(filePath);
8093
+ this.stop();
8094
+ try {
8095
+ this.process = spawn2("npx", ["serve", dir, "-l", String(STATIC_PORT), "--no-clipboard"], {
8096
+ stdio: "ignore",
8097
+ detached: true
8098
+ });
8099
+ this.process.unref();
8100
+ this.currentDir = dir;
8101
+ this.isDetached = true;
8102
+ const url = `http://localhost:${STATIC_PORT}/${fileName}`;
8103
+ console.log(`[PreviewServer] Serving ${dir} on port ${STATIC_PORT}`);
8104
+ return url;
8105
+ } catch (e) {
8106
+ console.log(`[PreviewServer] Failed to start static serve: ${e}`);
8107
+ return void 0;
8108
+ }
8109
+ }
8110
+ /**
8111
+ * Mode 2: Run a command (e.g. "python app.py") and use a controlled port.
8112
+ * The agent-specified port is ALWAYS replaced with COMMAND_PORT to prevent
8113
+ * conflicts with the host system (e.g. Next.js on 3000).
8114
+ * Returns the preview URL.
8115
+ */
8116
+ runCommand(cmd, cwd, agentPort) {
8117
+ this.stop();
8118
+ const port = COMMAND_PORT;
8119
+ cmd = cmd.replace(/\s+(?:--port|-p)\s+\d+/gi, "");
8120
+ if (agentPort) cmd = cmd.replace(new RegExp(`\\b${agentPort}\\b`, "g"), String(port));
8121
+ cmd = `${cmd} --port ${port}`;
8122
+ console.log(`[PreviewServer] Command: "${cmd}" (forced port ${port})`);
8123
+ this.killPortHolder(port);
8124
+ try {
8125
+ this.process = spawn2(cmd, {
8126
+ shell: true,
8127
+ cwd,
8128
+ stdio: "ignore",
8129
+ detached: true,
8130
+ env: { ...process.env, PORT: String(port) }
8131
+ });
8132
+ this.process.unref();
8133
+ this.currentDir = cwd;
8134
+ this.isDetached = true;
8135
+ const url = `http://localhost:${port}`;
8136
+ console.log(`[PreviewServer] Running "${cmd}" in ${cwd}, preview at port ${port}`);
8137
+ return url;
8138
+ } catch (e) {
8139
+ console.log(`[PreviewServer] Failed to run command: ${e}`);
8140
+ return void 0;
8141
+ }
8142
+ }
8143
+ /**
8144
+ * Mode 3: Launch a desktop/CLI process (no web preview URL).
8145
+ * Used for Pygame, Tkinter, Electron, terminal apps, etc.
8146
+ * NOT detached — GUI apps need the login session to access WindowServer (macOS).
8147
+ */
8148
+ launchProcess(cmd, cwd) {
8149
+ this.stop();
8150
+ try {
8151
+ this.process = spawn2(cmd, {
8152
+ shell: true,
8153
+ cwd,
8154
+ stdio: ["ignore", "ignore", "pipe"]
8155
+ });
8156
+ this.currentDir = cwd;
8157
+ this.isDetached = false;
8158
+ console.log(`[PreviewServer] Launched "${cmd}" in ${cwd} (pid=${this.process.pid})`);
8159
+ this.process.stderr?.on("data", (data) => {
8160
+ const msg = data.toString().trim();
8161
+ if (msg) console.log(`[PreviewServer] stderr: ${msg.slice(0, 200)}`);
8162
+ });
8163
+ this.process.on("exit", (code) => {
8164
+ console.log(`[PreviewServer] Process exited with code ${code}`);
8165
+ });
8166
+ } catch (e) {
8167
+ console.log(`[PreviewServer] Failed to launch process: ${e}`);
8168
+ }
8169
+ }
8170
+ /** Kill the current process and any orphan process on managed ports */
8171
+ stop() {
8172
+ if (this.process) {
8173
+ try {
8174
+ if (this.isDetached && this.process.pid) {
8175
+ process.kill(-this.process.pid, "SIGTERM");
8176
+ } else {
8177
+ this.process.kill("SIGTERM");
8178
+ }
8179
+ } catch {
8180
+ try {
8181
+ this.process.kill("SIGTERM");
8182
+ } catch {
8183
+ }
8184
+ }
8185
+ this.process = null;
8186
+ this.currentDir = null;
8187
+ this.isDetached = false;
8188
+ console.log(`[PreviewServer] Stopped`);
8189
+ }
8190
+ this.killPortHolder(STATIC_PORT);
8191
+ this.killPortHolder(COMMAND_PORT);
8192
+ }
8193
+ /** Kill whatever process is listening on the given port (best-effort). */
8194
+ killPortHolder(port) {
8195
+ try {
8196
+ const out = execSync4(`lsof -ti :${port}`, { encoding: "utf-8", timeout: 3e3 }).trim();
8197
+ if (out) {
8198
+ for (const pid of out.split("\n")) {
8199
+ const n = parseInt(pid, 10);
8200
+ if (n > 0) {
8201
+ try {
8202
+ process.kill(n, "SIGKILL");
8203
+ } catch {
8204
+ }
8205
+ }
8206
+ }
8207
+ console.log(`[PreviewServer] Killed orphan process(es) on port ${port}: ${out.replace(/\n/g, ", ")}`);
8208
+ }
8209
+ } catch {
8210
+ }
8211
+ }
8212
+ };
8213
+ var previewServer = new PreviewServer();
8214
+
8213
8215
  // ../../packages/orchestrator/src/index.ts
8214
8216
  function createOrchestrator(options) {
8215
8217
  return new Orchestrator(options);
@@ -8799,6 +8801,15 @@ function archiveProject(agents, team) {
8799
8801
  }
8800
8802
  }
8801
8803
  }
8804
+ let totalInputTokens = 0;
8805
+ let totalOutputTokens = 0;
8806
+ for (const e of projectEvents) {
8807
+ if (e.type === "TASK_DONE" && e.result?.tokenUsage) {
8808
+ totalInputTokens += e.result.tokenUsage.inputTokens ?? 0;
8809
+ totalOutputTokens += e.result.tokenUsage.outputTokens ?? 0;
8810
+ }
8811
+ }
8812
+ const tokenUsage = totalInputTokens > 0 || totalOutputTokens > 0 ? { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } : void 0;
8802
8813
  const id = `${projectStartedAt}-${projectName || "project"}`;
8803
8814
  const archive = {
8804
8815
  id,
@@ -8808,7 +8819,8 @@ function archiveProject(agents, team) {
8808
8819
  agents,
8809
8820
  team,
8810
8821
  events: projectEvents,
8811
- preview
8822
+ preview,
8823
+ tokenUsage
8812
8824
  };
8813
8825
  try {
8814
8826
  const filePath = path12.join(PROJECTS_DIR, `${id}.json`);
@@ -8836,7 +8848,8 @@ function listProjects() {
8836
8848
  endedAt: raw.endedAt,
8837
8849
  agentNames: raw.agents.map((a) => a.name),
8838
8850
  eventCount: raw.events.length,
8839
- preview: raw.preview
8851
+ preview: raw.preview,
8852
+ tokenUsage: raw.tokenUsage
8840
8853
  });
8841
8854
  } catch {
8842
8855
  }
@@ -9015,6 +9028,8 @@ function mapOrchestratorEvent(e) {
9015
9028
  persistTeamState();
9016
9029
  return null;
9017
9030
  }
9031
+ case "token:update":
9032
+ return { type: "TOKEN_UPDATE", agentId: e.agentId, inputTokens: e.inputTokens, outputTokens: e.outputTokens };
9018
9033
  // New events (worktree, retry) — log only, no wire protocol equivalent yet
9019
9034
  case "task:retrying":
9020
9035
  console.log(`[Retry] Agent ${e.agentId} retrying task ${e.taskId} (attempt ${e.attempt}/${e.maxRetries})`);
@@ -9541,6 +9556,7 @@ async function main() {
9541
9556
  orc.on("task:queued", forwardEvent);
9542
9557
  orc.on("worktree:created", forwardEvent);
9543
9558
  orc.on("worktree:merged", forwardEvent);
9559
+ orc.on("token:update", forwardEvent);
9544
9560
  orc.on("agent:created", forwardEvent);
9545
9561
  orc.on("agent:fired", forwardEvent);
9546
9562
  orc.on("task:result-returned", forwardEvent);