heyatlas 1.3.3 → 1.3.5

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.
Files changed (2) hide show
  1. package/dist/cli.js +221 -117
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -4815,6 +4815,15 @@ async function pollForToken(deviceCode, interval) {
4815
4815
  }
4816
4816
  }
4817
4817
  async function login() {
4818
+ const envToken = process.env.HEYATLAS_ACCESS_TOKEN;
4819
+ const envUserId = process.env.HEYATLAS_USER_ID;
4820
+ if (envToken && envUserId) {
4821
+ return {
4822
+ accessToken: envToken,
4823
+ userId: envUserId,
4824
+ email: "sandbox@heyatlas.app"
4825
+ };
4826
+ }
4818
4827
  const existing = loadCredentials();
4819
4828
  if (existing) {
4820
4829
  return existing;
@@ -52200,104 +52209,148 @@ class ACPProviderAgent {
52200
52209
  }
52201
52210
 
52202
52211
  // agents/config.ts
52203
- function isHTTPAgent(agent) {
52204
- return agent === "agent-smith-py";
52212
+ function isSmith(agent) {
52213
+ return agent === "smith";
52205
52214
  }
52206
52215
 
52207
- // agents/http-agent.ts
52216
+ // agents/smith.ts
52208
52217
  import { spawn as spawn2 } from "child_process";
52209
- var HTTP_AGENT_CONFIG = {
52210
- "agent-smith-py": {
52211
- command: "uv",
52212
- args: ["run", "python", "main.py"],
52213
- port: 3141,
52214
- healthPath: "/health",
52215
- taskPath: "/agents/agent-smith/text",
52216
- streamPath: "/agents/agent-smith/stream",
52217
- cwd: "desktop-sandbox/agent-smith-py"
52218
- }
52219
- };
52220
-
52221
- class HTTPAgent {
52222
- agentType;
52218
+ var SANDBOX_BUNDLE_PATH = "/home/user/agents/agent-smith.cjs";
52219
+ var SMITH_CONFIG = {
52220
+ port: 3141,
52221
+ agentId: "workflow-orchestrator",
52222
+ healthPath: "/agents",
52223
+ chatPath: "/agents/workflow-orchestrator/chat",
52224
+ command: "npx",
52225
+ args: ["tsx", "src/index.ts"],
52226
+ cwd: "smith",
52227
+ sandboxCommand: "node",
52228
+ sandboxArgs: [SANDBOX_BUNDLE_PATH]
52229
+ };
52230
+
52231
+ class Smith {
52223
52232
  options;
52224
- process = null;
52225
52233
  baseUrl;
52226
- config;
52227
- constructor(agentType, options = {}) {
52228
- this.agentType = agentType;
52234
+ process = null;
52235
+ constructor(options = {}) {
52229
52236
  this.options = options;
52230
- this.config = HTTP_AGENT_CONFIG[agentType];
52231
- this.baseUrl = `http://localhost:${this.config.port}`;
52237
+ this.baseUrl = `http://localhost:${SMITH_CONFIG.port}`;
52232
52238
  }
52233
52239
  get name() {
52234
- return this.agentType;
52240
+ return "smith";
52241
+ }
52242
+ get agentId() {
52243
+ return SMITH_CONFIG.agentId;
52235
52244
  }
52236
52245
  get port() {
52237
- return this.config.port;
52246
+ return SMITH_CONFIG.port;
52247
+ }
52248
+ async isRunning() {
52249
+ try {
52250
+ const response = await fetch(`${this.baseUrl}${SMITH_CONFIG.healthPath}`, {
52251
+ method: "GET",
52252
+ signal: AbortSignal.timeout(2000)
52253
+ });
52254
+ return response.ok;
52255
+ } catch {
52256
+ return false;
52257
+ }
52238
52258
  }
52239
52259
  async isAvailable() {
52240
52260
  try {
52241
52261
  const { promisify } = await import("node:util");
52242
52262
  const exec = promisify((await import("node:child_process")).exec);
52243
- await exec("which uv || where uv");
52263
+ await exec("which npx || where npx");
52264
+ return true;
52265
+ } catch {
52266
+ return false;
52267
+ }
52268
+ }
52269
+ async isSandboxMode() {
52270
+ try {
52271
+ const fs = await import("node:fs/promises");
52272
+ await fs.access(SANDBOX_BUNDLE_PATH);
52244
52273
  return true;
52245
52274
  } catch {
52246
52275
  return false;
52247
52276
  }
52248
52277
  }
52249
52278
  async start() {
52279
+ if (await this.isRunning()) {
52280
+ console.log(`[smith] Server already running on port ${SMITH_CONFIG.port}`);
52281
+ return;
52282
+ }
52283
+ const isSandbox = await this.isSandboxMode();
52284
+ let command;
52285
+ let args;
52286
+ let cwd;
52287
+ if (isSandbox) {
52288
+ command = SMITH_CONFIG.sandboxCommand;
52289
+ args = SMITH_CONFIG.sandboxArgs;
52290
+ cwd = "/home/user/agents";
52291
+ console.log(`[smith] Running in sandbox mode with pre-built bundle`);
52292
+ } else {
52293
+ const baseCwd = this.options.cwd || process.cwd();
52294
+ const path = await import("node:path");
52295
+ const fs = await import("node:fs/promises");
52296
+ let smithPath = path.join(baseCwd, SMITH_CONFIG.cwd);
52297
+ try {
52298
+ await fs.access(smithPath);
52299
+ } catch {
52300
+ smithPath = path.join(baseCwd, "..", SMITH_CONFIG.cwd);
52301
+ }
52302
+ command = SMITH_CONFIG.command;
52303
+ args = SMITH_CONFIG.args;
52304
+ cwd = smithPath;
52305
+ console.log(`[smith] Running in development mode from: ${cwd}`);
52306
+ }
52250
52307
  const env = {
52251
52308
  ...process.env,
52252
- PYTHONUNBUFFERED: "1"
52309
+ PORT: String(SMITH_CONFIG.port)
52253
52310
  };
52254
- const cwd = this.config.cwd ? `${this.options.cwd || process.cwd()}/${this.config.cwd}` : this.options.cwd || process.cwd();
52255
- this.process = spawn2(this.config.command, [...this.config.args, "--port", String(this.config.port)], {
52311
+ this.process = spawn2(command, args, {
52256
52312
  cwd,
52257
52313
  env,
52258
- stdio: ["pipe", "pipe", "pipe"]
52314
+ stdio: ["pipe", "pipe", "pipe"],
52315
+ shell: true
52259
52316
  });
52260
52317
  this.process.stdout?.on("data", (data) => {
52261
- console.log(`[${this.agentType}] ${data.toString().trim()}`);
52318
+ console.log(`[smith] ${data.toString().trim()}`);
52262
52319
  });
52263
52320
  this.process.stderr?.on("data", (data) => {
52264
- console.error(`[${this.agentType}] ${data.toString().trim()}`);
52321
+ console.error(`[smith] ${data.toString().trim()}`);
52322
+ });
52323
+ this.process.on("error", (err) => {
52324
+ console.error(`[smith] Process error:`, err.message);
52265
52325
  });
52266
52326
  await this.waitForReady();
52267
52327
  }
52268
- async waitForReady(maxAttempts = 30) {
52328
+ async waitForReady(maxAttempts = 60) {
52269
52329
  const delay2 = (ms) => new Promise((r) => setTimeout(r, ms));
52270
52330
  for (let i = 0;i < maxAttempts; i++) {
52271
- try {
52272
- const response = await fetch(`${this.baseUrl}${this.config.healthPath}`);
52273
- if (response.ok) {
52274
- return;
52275
- }
52276
- } catch {}
52331
+ if (await this.isRunning()) {
52332
+ return;
52333
+ }
52277
52334
  await delay2(1000);
52278
52335
  }
52279
- throw new Error(`Agent ${this.agentType} failed to start after ${maxAttempts}s`);
52336
+ throw new Error(`Smith failed to start after ${maxAttempts}s`);
52280
52337
  }
52281
- async executeTask(prompt, taskId) {
52282
- const response = await fetch(`${this.baseUrl}${this.config.taskPath}`, {
52283
- method: "POST",
52284
- headers: { "Content-Type": "application/json" },
52285
- body: JSON.stringify({ prompt, task_id: taskId })
52286
- });
52287
- if (!response.ok) {
52288
- throw new Error(`Task failed: ${response.statusText}`);
52338
+ stop() {
52339
+ if (this.process) {
52340
+ this.process.kill("SIGTERM");
52341
+ this.process = null;
52289
52342
  }
52290
- const data = await response.json();
52291
- return data;
52292
52343
  }
52293
- async* executeTaskStream(prompt, taskId) {
52294
- const response = await fetch(`${this.baseUrl}${this.config.streamPath}`, {
52344
+ async* streamChat(prompt) {
52345
+ const response = await fetch(`${this.baseUrl}${SMITH_CONFIG.chatPath}`, {
52295
52346
  method: "POST",
52296
52347
  headers: { "Content-Type": "application/json" },
52297
- body: JSON.stringify({ prompt, task_id: taskId })
52348
+ body: JSON.stringify({
52349
+ messages: [{ role: "user", content: prompt }]
52350
+ })
52298
52351
  });
52299
52352
  if (!response.ok) {
52300
- throw new Error(`Stream failed: ${response.statusText}`);
52353
+ throw new Error(`Chat request failed: ${response.statusText}`);
52301
52354
  }
52302
52355
  const reader = response.body?.getReader();
52303
52356
  if (!reader) {
@@ -52314,28 +52367,32 @@ class HTTPAgent {
52314
52367
  `);
52315
52368
  buffer = lines.pop() || "";
52316
52369
  for (const line of lines) {
52317
- if (line.startsWith("data: ")) {
52370
+ const trimmed = line.trim();
52371
+ if (!trimmed)
52372
+ continue;
52373
+ let jsonStr = null;
52374
+ if (trimmed.startsWith("data: ")) {
52375
+ jsonStr = trimmed.slice(6);
52376
+ } else if (/^\d+:/.test(trimmed)) {
52377
+ const colonIndex = trimmed.indexOf(":");
52378
+ jsonStr = trimmed.slice(colonIndex + 1);
52379
+ }
52380
+ if (jsonStr && jsonStr !== "[DONE]") {
52318
52381
  try {
52319
- const event = JSON.parse(line.slice(6));
52382
+ const event = JSON.parse(jsonStr);
52320
52383
  yield event;
52321
52384
  } catch {}
52322
52385
  }
52323
52386
  }
52324
52387
  }
52325
52388
  }
52326
- stop() {
52327
- if (this.process) {
52328
- this.process.kill("SIGTERM");
52329
- this.process = null;
52330
- }
52331
- }
52332
52389
  }
52333
52390
 
52334
52391
  // commands/connect.ts
52335
52392
  async function connect(agentType, options = {}) {
52336
52393
  const credentials = await login();
52337
- if (isHTTPAgent(agentType)) {
52338
- return connectHTTPAgent(agentType, credentials, options);
52394
+ if (isSmith(agentType)) {
52395
+ return connectSmith(credentials, options);
52339
52396
  }
52340
52397
  if (isACPAgent(agentType)) {
52341
52398
  return connectACPAgent(agentType, credentials, options);
@@ -52343,18 +52400,18 @@ async function connect(agentType, options = {}) {
52343
52400
  console.error(`Error: Unknown agent type '${agentType}'`);
52344
52401
  process.exit(1);
52345
52402
  }
52346
- async function connectHTTPAgent(agentType, credentials, options) {
52347
- const agent = new HTTPAgent(agentType, { cwd: process.cwd() });
52403
+ async function connectSmith(credentials, options) {
52404
+ const agent = new Smith({ cwd: process.cwd() });
52348
52405
  const available = await agent.isAvailable();
52349
52406
  if (!available) {
52350
- console.error(`Error: Python not found. Install Python 3.11+ to use ${agentType}`);
52407
+ console.error("Error: npx not found. Install Node.js to use smith");
52351
52408
  process.exit(1);
52352
52409
  }
52353
- console.log(`Agent: ${agentType} (HTTP mode)`);
52410
+ console.log("Agent: smith");
52354
52411
  try {
52355
- console.log("Starting agent server...");
52412
+ console.log("Starting smith server...");
52356
52413
  await agent.start();
52357
- console.log(`Agent server running on port ${agent.port}`);
52414
+ console.log(`Smith server running on port ${agent.port}`);
52358
52415
  } catch (error87) {
52359
52416
  console.error(`Failed to start agent: ${error87}`);
52360
52417
  process.exit(1);
@@ -52365,66 +52422,113 @@ async function connectHTTPAgent(agentType, credentials, options) {
52365
52422
  interactive: true
52366
52423
  });
52367
52424
  tunnel.onNewTask(async (task) => {
52368
- const prompt = task.description || "Hello";
52369
- console.log(`Task: ${prompt.slice(0, 50)}...`);
52425
+ const { prompt, latestUserMessage } = buildPromptWithContext(task);
52426
+ const isNewTask = task.state === "new";
52427
+ console.log(`${isNewTask ? "New" : "Continue"}: ${latestUserMessage.slice(0, 50)}...`);
52370
52428
  await tunnel.updateTask(task.id, { state: "in-progress" });
52371
52429
  await tunnel.appendContext(task.id, [
52372
52430
  {
52373
52431
  type: "message",
52374
52432
  timestamp: Date.now(),
52375
- data: { role: "user", content: prompt }
52433
+ data: { role: "user", content: latestUserMessage }
52376
52434
  }
52377
52435
  ]);
52378
- await tunnel.broadcastTaskEvent(task.id, {
52379
- type: "workforce_event",
52380
- timestamp: Date.now(),
52381
- data: { event_type: "task_started", task_content: prompt }
52382
- });
52383
52436
  try {
52384
- let finalResult = "Task completed";
52385
- let finalStatus = "completed";
52386
52437
  const parts = [];
52387
- for await (const event of agent.executeTaskStream(prompt, task.id)) {
52438
+ const toolCalls = new Map;
52439
+ const activeReasoningParts = new Map;
52440
+ for await (const chunk of agent.streamChat(prompt)) {
52388
52441
  await tunnel.broadcastTaskEvent(task.id, {
52389
- type: "workforce_event",
52442
+ type: "ui_stream_chunk",
52390
52443
  timestamp: Date.now(),
52391
- data: event
52444
+ data: chunk
52392
52445
  });
52393
- if (event.event_type === "result") {
52394
- finalResult = event.result || event.error || "Task completed";
52395
- finalStatus = event.status || "completed";
52396
- }
52397
- if (event.event_type === "task_assigned") {
52398
- console.log(` → Assigned to: ${event.worker_name?.slice(0, 40)}...`);
52399
- } else if (event.event_type === "task_decomposed") {
52400
- console.log(` → Decomposed into ${event.subtasks?.length || 0} subtasks`);
52446
+ switch (chunk.type) {
52447
+ case "text-delta": {
52448
+ const existingText = parts.find((p) => p.type === "text");
52449
+ if (existingText && "text" in existingText) {
52450
+ existingText.text += chunk.delta || "";
52451
+ } else {
52452
+ parts.push({ type: "text", text: chunk.delta || "" });
52453
+ }
52454
+ break;
52455
+ }
52456
+ case "reasoning-start": {
52457
+ const reasoningPart = {
52458
+ type: "reasoning",
52459
+ text: "",
52460
+ state: "streaming"
52461
+ };
52462
+ activeReasoningParts.set(chunk.id || "default", reasoningPart);
52463
+ parts.push(reasoningPart);
52464
+ break;
52465
+ }
52466
+ case "reasoning-delta":
52467
+ case "reasoning": {
52468
+ const id = chunk.id || "default";
52469
+ let reasoningPart = activeReasoningParts.get(id);
52470
+ if (!reasoningPart) {
52471
+ reasoningPart = { type: "reasoning", text: "", state: "streaming" };
52472
+ activeReasoningParts.set(id, reasoningPart);
52473
+ parts.push(reasoningPart);
52474
+ }
52475
+ reasoningPart.text += chunk.delta || chunk.text || "";
52476
+ break;
52477
+ }
52478
+ case "reasoning-end": {
52479
+ const id = chunk.id || "default";
52480
+ const reasoningPart = activeReasoningParts.get(id);
52481
+ if (reasoningPart) {
52482
+ reasoningPart.state = "done";
52483
+ activeReasoningParts.delete(id);
52484
+ }
52485
+ break;
52486
+ }
52487
+ case "tool-input-available": {
52488
+ const input = chunk.input;
52489
+ const realToolName = input?.toolName || chunk.toolName || "tool";
52490
+ const realArgs = input?.args || input || {};
52491
+ toolCalls.set(chunk.toolCallId || "", {
52492
+ type: "dynamic-tool",
52493
+ toolCallId: chunk.toolCallId,
52494
+ toolName: realToolName,
52495
+ state: "input-available",
52496
+ input: realArgs,
52497
+ subAgentName: chunk.subAgentName
52498
+ });
52499
+ parts.push(toolCalls.get(chunk.toolCallId || ""));
52500
+ break;
52501
+ }
52502
+ case "tool-output-available": {
52503
+ const existing = toolCalls.get(chunk.toolCallId || "");
52504
+ if (existing) {
52505
+ const updated = { ...existing, state: "output-available", output: chunk.output };
52506
+ toolCalls.set(chunk.toolCallId || "", updated);
52507
+ const idx = parts.findIndex((p) => p.type === "dynamic-tool" && p.toolCallId === chunk.toolCallId);
52508
+ if (idx >= 0)
52509
+ parts[idx] = updated;
52510
+ }
52511
+ break;
52512
+ }
52401
52513
  }
52402
52514
  }
52403
- await tunnel.appendContext(task.id, [
52404
- {
52405
- type: "ui_message",
52406
- timestamp: Date.now(),
52407
- data: {
52408
- id: crypto.randomUUID(),
52409
- role: "assistant",
52410
- parts: [{ type: "text", text: finalResult }]
52515
+ if (parts.length > 0) {
52516
+ await tunnel.appendContext(task.id, [
52517
+ {
52518
+ type: "ui_message",
52519
+ timestamp: Date.now(),
52520
+ data: { id: crypto.randomUUID(), role: "assistant", parts }
52411
52521
  }
52412
- }
52413
- ]);
52414
- await tunnel.updateTask(task.id, {
52415
- state: finalStatus === "completed" ? "completed" : "failed",
52416
- result: finalResult
52417
- });
52418
- console.log(`Task ${finalStatus}`);
52522
+ ]);
52523
+ }
52524
+ await tunnel.updateTask(task.id, { state: "completed", result: "end_turn" });
52525
+ console.log("Task completed");
52419
52526
  } catch (error87) {
52420
52527
  console.error(`Task failed: ${error87}`);
52421
- await tunnel.updateTask(task.id, {
52422
- state: "failed",
52423
- result: String(error87)
52424
- });
52528
+ await tunnel.updateTask(task.id, { state: "failed", result: String(error87) });
52425
52529
  }
52426
52530
  });
52427
- await tunnel.connect(credentials.userId, agentType);
52531
+ await tunnel.connect(credentials.userId, "smith");
52428
52532
  console.log("Tunnel established");
52429
52533
  const voiceUrl = `${process.env.HEYATLAS_API || "https://heyatlas.app"}/chat`;
52430
52534
  if (options.openBrowser !== false) {
@@ -52435,7 +52539,7 @@ async function connectHTTPAgent(agentType, credentials, options) {
52435
52539
  } catch {}
52436
52540
  }
52437
52541
  console.log(`
52438
- HeyAtlas connected to ${agentType}`);
52542
+ HeyAtlas connected to smith`);
52439
52543
  console.log(`Continue here: ${voiceUrl}`);
52440
52544
  console.log(`
52441
52545
  Press Ctrl+C to disconnect
@@ -52670,8 +52774,8 @@ var ACP_AGENTS = [
52670
52774
  "openhands",
52671
52775
  "cagent"
52672
52776
  ];
52673
- var HTTP_AGENTS = ["agent-smith-py"];
52674
- var SUPPORTED_AGENTS = [...ACP_AGENTS, ...HTTP_AGENTS];
52777
+ var SMITH_AGENTS = ["smith"];
52778
+ var SUPPORTED_AGENTS = [...ACP_AGENTS, ...SMITH_AGENTS];
52675
52779
  var { positionals, values } = parseArgs({
52676
52780
  args: process.argv.slice(2),
52677
52781
  options: {
@@ -52690,7 +52794,7 @@ Usage:
52690
52794
 
52691
52795
  Supported Agents:
52692
52796
  ACP Agents: ${ACP_AGENTS.join(", ")}
52693
- HTTP Agents: ${HTTP_AGENTS.join(", ")}
52797
+ Smith: ${SMITH_AGENTS.join(", ")}
52694
52798
 
52695
52799
  Options:
52696
52800
  -h, --help Show this help message
@@ -52698,8 +52802,8 @@ Options:
52698
52802
  --no-browser Don't open browser automatically
52699
52803
 
52700
52804
  Examples:
52701
- heyatlas connect opencode Connect OpenCode via ACP
52702
- heyatlas connect agent-smith-py Connect Agent Smith (CAMEL-AI workforce)
52805
+ heyatlas connect opencode Connect OpenCode via ACP
52806
+ heyatlas connect smith Connect Smith
52703
52807
  `);
52704
52808
  }
52705
52809
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heyatlas",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "Tunnel local AI agents to the cloud for voice-powered interactions",
5
5
  "author": "Bishwendu Kundu <bishwenduk029@gmail.com>",
6
6
  "license": "MIT",