agdi 3.3.2 → 3.3.4

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.
@@ -0,0 +1,45 @@
1
+ // src/core/event-bus.ts
2
+ import { EventEmitter } from "events";
3
+ var agentEventBus = new EventEmitter();
4
+ agentEventBus.setMaxListeners(50);
5
+ var thoughtFlushTimeout = null;
6
+ var pendingThought = null;
7
+ function emitAgentEvent(event) {
8
+ const fullEvent = {
9
+ ...event,
10
+ timestamp: Date.now()
11
+ };
12
+ if (event.type === "thought") {
13
+ pendingThought = fullEvent;
14
+ if (!thoughtFlushTimeout) {
15
+ thoughtFlushTimeout = setTimeout(() => {
16
+ if (pendingThought) {
17
+ agentEventBus.emit("agent_event", pendingThought);
18
+ pendingThought = null;
19
+ }
20
+ thoughtFlushTimeout = null;
21
+ }, 100);
22
+ }
23
+ } else {
24
+ agentEventBus.emit("agent_event", fullEvent);
25
+ }
26
+ }
27
+ function cleanupEventBus() {
28
+ agentEventBus.removeAllListeners();
29
+ if (thoughtFlushTimeout) {
30
+ clearTimeout(thoughtFlushTimeout);
31
+ }
32
+ }
33
+ process.on("exit", cleanupEventBus);
34
+ process.on("SIGINT", cleanupEventBus);
35
+ process.on("uncaughtException", (err) => {
36
+ console.error("Uncaught exception:", err);
37
+ cleanupEventBus();
38
+ process.exit(1);
39
+ });
40
+
41
+ export {
42
+ agentEventBus,
43
+ emitAgentEvent,
44
+ cleanupEventBus
45
+ };
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  agentEventBus,
3
+ cleanupEventBus,
3
4
  emitAgentEvent
4
- } from "./chunk-AEWEBMJY.js";
5
+ } from "./chunk-A5UTYKKM.js";
5
6
  export {
6
7
  agentEventBus,
8
+ cleanupEventBus,
7
9
  emitAgentEvent
8
10
  };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  agentEventBus,
4
4
  emitAgentEvent
5
- } from "./chunk-AEWEBMJY.js";
5
+ } from "./chunk-A5UTYKKM.js";
6
6
  import {
7
7
  loadConfig,
8
8
  saveConfig
@@ -343,9 +343,9 @@ var errorGradient = gradient([THEME.red, "#b91c1c"]);
343
343
  var goldGradient = gradient([THEME.yellow, "#fbbf24"]);
344
344
  async function renderBanner(version = "v2.6.0") {
345
345
  console.clear();
346
- const text = await new Promise((resolve) => {
346
+ const text = await new Promise((resolve2) => {
347
347
  figlet("AGDI", { font: "Slant" }, (err, data) => {
348
- resolve(data || "AGDI");
348
+ resolve2(data || "AGDI");
349
349
  });
350
350
  });
351
351
  console.log(brandGradient.multiline(text));
@@ -1576,7 +1576,7 @@ async function runProject(targetDir) {
1576
1576
  console.log(chalk6.yellow("\u{1F4E6} Installing dependencies...\n"));
1577
1577
  const installSpinner = ora3("Running npm install...").start();
1578
1578
  try {
1579
- await new Promise((resolve, reject) => {
1579
+ await new Promise((resolve2, reject) => {
1580
1580
  const install = spawn("npm", ["install"], {
1581
1581
  cwd: absoluteDir,
1582
1582
  stdio: "inherit",
@@ -1585,7 +1585,7 @@ async function runProject(targetDir) {
1585
1585
  install.on("close", (code) => {
1586
1586
  if (code === 0) {
1587
1587
  installSpinner.succeed("Dependencies installed!");
1588
- resolve();
1588
+ resolve2();
1589
1589
  } else {
1590
1590
  installSpinner.fail("npm install failed");
1591
1591
  reject(new Error(`npm install exited with code ${code}`));
@@ -2101,8 +2101,9 @@ var BaseAgent = class {
2101
2101
  }
2102
2102
  /**
2103
2103
  * Generate a response from the LLM
2104
+ * Includes timeout protection to prevent indefinite hanging
2104
2105
  */
2105
- async think(prompt) {
2106
+ async think(prompt, timeoutMs = 12e4) {
2106
2107
  this.log("Thinking...", "info");
2107
2108
  emitAgentEvent({
2108
2109
  type: "thought",
@@ -2110,7 +2111,12 @@ var BaseAgent = class {
2110
2111
  role: this.role,
2111
2112
  message: `Analyzing task...`
2112
2113
  });
2113
- const response = await this.llm.generate(prompt, this.getSystemPrompt());
2114
+ const response = await Promise.race([
2115
+ this.llm.generate(prompt, this.getSystemPrompt()),
2116
+ new Promise(
2117
+ (_, reject) => setTimeout(() => reject(new Error(`LLM request timed out after ${timeoutMs / 1e3}s`)), timeoutMs)
2118
+ )
2119
+ ]);
2114
2120
  const summary = response.text.split("\n")[0].substring(0, 80) + "...";
2115
2121
  emitAgentEvent({
2116
2122
  type: "thought",
@@ -3148,12 +3154,17 @@ Requirements:
3148
3154
  }
3149
3155
  try {
3150
3156
  const prodFlag = production ? "--prod" : "";
3151
- const command = `npx vercel ${prodFlag} --yes --token ${this.vercelToken}`;
3152
- this.log(`Running: npx vercel ${prodFlag} --yes --token ***`, "info");
3157
+ const command = `npx vercel ${prodFlag} --yes`;
3158
+ this.log(`Running: npx vercel ${prodFlag} --yes`, "info");
3153
3159
  const { stdout, stderr } = await execAsync2(command, {
3154
3160
  cwd,
3155
- timeout: 3e5
3161
+ timeout: 3e5,
3156
3162
  // 5 minutes
3163
+ env: {
3164
+ ...process.env,
3165
+ VERCEL_TOKEN: this.vercelToken
3166
+ // Pass via env, not CLI (security)
3167
+ }
3157
3168
  });
3158
3169
  const output = stdout + stderr;
3159
3170
  const urlMatch = output.match(/https:\/\/[^\s]+\.vercel\.app/);
@@ -3203,10 +3214,15 @@ Requirements:
3203
3214
  try {
3204
3215
  const prodFlag = production ? "--prod" : "";
3205
3216
  const outputDir = await detectBuildOutputDir(cwd);
3206
- const command = `npx netlify deploy ${prodFlag} --auth ${this.netlifyToken} --dir=${outputDir}`;
3217
+ const command = `npx netlify deploy ${prodFlag} --dir=${outputDir}`;
3207
3218
  const { stdout, stderr } = await execAsync2(command, {
3208
3219
  cwd,
3209
- timeout: 3e5
3220
+ timeout: 3e5,
3221
+ env: {
3222
+ ...process.env,
3223
+ NETLIFY_AUTH_TOKEN: this.netlifyToken
3224
+ // Pass via env, not CLI (security)
3225
+ }
3210
3226
  });
3211
3227
  const output = stdout + (stderr || "");
3212
3228
  const urlMatch = output.match(/https:\/\/[^\s]+\.netlify\.app/);
@@ -3592,7 +3608,7 @@ ${qaResult.errors?.join("\n")}`,
3592
3608
  */
3593
3609
  async executeTask(task) {
3594
3610
  this.log(`[${task.assignee}] ${task.title}`, "task");
3595
- const { emitAgentEvent: emitAgentEvent2 } = await import("./event-bus-EDC4P3T5.js");
3611
+ const { emitAgentEvent: emitAgentEvent2 } = await import("./event-bus-Q3WCETQQ.js");
3596
3612
  emitAgentEvent2({
3597
3613
  type: "handoff",
3598
3614
  agentName: task.assignee,
@@ -3647,10 +3663,21 @@ ${qaResult.errors?.join("\n")}`,
3647
3663
  }
3648
3664
  /**
3649
3665
  * Write generated files to disk
3666
+ * SECURITY: All paths are validated to prevent directory traversal attacks
3650
3667
  */
3651
3668
  async writeFiles(files, filesCreated, filesModified) {
3669
+ const workspaceRoot = path5.resolve(this.config.workspaceRoot);
3652
3670
  for (const file of files) {
3653
- const fullPath = path5.join(this.config.workspaceRoot, file.path);
3671
+ const validationResult = this.validateFilePath(file.path, workspaceRoot);
3672
+ if (!validationResult.valid) {
3673
+ this.log(`\u{1F6E1}\uFE0F Blocked unsafe path: ${file.path} - ${validationResult.error}`, "error");
3674
+ await this.appendTrace("file_blocked", {
3675
+ path: file.path,
3676
+ reason: validationResult.error
3677
+ });
3678
+ continue;
3679
+ }
3680
+ const fullPath = validationResult.resolvedPath;
3654
3681
  const dir = path5.dirname(fullPath);
3655
3682
  try {
3656
3683
  await fs5.mkdir(dir, { recursive: true });
@@ -3665,7 +3692,7 @@ ${qaResult.errors?.join("\n")}`,
3665
3692
  }
3666
3693
  await fs5.writeFile(fullPath, file.content, "utf-8");
3667
3694
  const afterContent = file.content;
3668
- const outputPath = path5.join(this.runDir, "outputs", file.path);
3695
+ const outputPath = path5.join(this.runDir, "outputs", validationResult.normalizedPath);
3669
3696
  await fs5.mkdir(path5.dirname(outputPath), { recursive: true });
3670
3697
  await fs5.writeFile(outputPath, file.content, "utf-8");
3671
3698
  const beforeHash = beforeContent ? this.hashContent(beforeContent) : null;
@@ -3692,6 +3719,54 @@ ${qaResult.errors?.join("\n")}`,
3692
3719
  }
3693
3720
  }
3694
3721
  }
3722
+ /**
3723
+ * Validate a file path to prevent directory traversal attacks
3724
+ * SECURITY: Critical function - blocks paths that escape workspace root
3725
+ */
3726
+ validateFilePath(filePath, workspaceRoot) {
3727
+ if (path5.isAbsolute(filePath)) {
3728
+ return {
3729
+ valid: false,
3730
+ error: "Absolute paths not allowed"
3731
+ };
3732
+ }
3733
+ const normalizedPath = path5.normalize(filePath).replace(/^(\.\.[\\/])+/, "");
3734
+ const resolvedPath = path5.resolve(workspaceRoot, filePath);
3735
+ if (filePath.includes("..")) {
3736
+ if (!resolvedPath.startsWith(workspaceRoot + path5.sep) && resolvedPath !== workspaceRoot) {
3737
+ return {
3738
+ valid: false,
3739
+ error: "Path traversal blocked: cannot escape workspace root"
3740
+ };
3741
+ }
3742
+ }
3743
+ if (!resolvedPath.startsWith(workspaceRoot)) {
3744
+ return {
3745
+ valid: false,
3746
+ error: "Path traversal blocked: resolved path outside workspace"
3747
+ };
3748
+ }
3749
+ const sensitivePatterns = [
3750
+ /^\.git\//,
3751
+ /^node_modules\//,
3752
+ /^\.env$/,
3753
+ /^\.env\./,
3754
+ /\/\.git\//
3755
+ ];
3756
+ for (const pattern of sensitivePatterns) {
3757
+ if (pattern.test(normalizedPath)) {
3758
+ return {
3759
+ valid: false,
3760
+ error: `Blocked write to sensitive path: ${normalizedPath}`
3761
+ };
3762
+ }
3763
+ }
3764
+ return {
3765
+ valid: true,
3766
+ resolvedPath,
3767
+ normalizedPath
3768
+ };
3769
+ }
3695
3770
  async initializeRun(userPrompt) {
3696
3771
  this.runId = uuidv42();
3697
3772
  this.runDir = path5.join(this.config.workspaceRoot, "runs", this.runId);
@@ -4186,145 +4261,120 @@ import { render } from "ink";
4186
4261
  // src/ui/tui.tsx
4187
4262
  import { useState, useEffect } from "react";
4188
4263
  import { Box, Text, useApp } from "ink";
4189
- import BigText from "ink-big-text";
4190
- import Spinner from "ink-spinner";
4191
4264
  import TextInput from "ink-text-input";
4192
4265
  import fs7 from "fs";
4193
4266
  import path7 from "path";
4194
4267
  import os from "os";
4195
4268
  import { jsx, jsxs } from "react/jsx-runtime";
4269
+ var THEME2 = {
4270
+ bg: "black",
4271
+ border: "gray",
4272
+ accent: "blue",
4273
+ text: "white",
4274
+ dim: "gray",
4275
+ success: "green"
4276
+ };
4196
4277
  var BootScreen = ({ onComplete }) => {
4197
4278
  const [progress, setProgress] = useState(0);
4198
- const [log, setLog] = useState("");
4199
- const logs = [
4200
- "Initializing core systems...",
4201
- "Loading neural modules...",
4202
- "Connecting to Agdi Cloud...",
4203
- "Verifying agent credentials...",
4204
- "Mounting virtual filesystem...",
4205
- "Establishing secure uplink...",
4206
- "Syncing global state...",
4207
- "Boot sequence complete."
4208
- ];
4209
4279
  useEffect(() => {
4210
4280
  const timer = setInterval(() => {
4211
4281
  setProgress((prev) => {
4212
- const next = prev + 5;
4282
+ const next = prev + 10;
4213
4283
  if (next >= 100) {
4214
4284
  clearInterval(timer);
4215
- setTimeout(onComplete, 200);
4285
+ setTimeout(onComplete, 100);
4216
4286
  return 100;
4217
4287
  }
4218
4288
  return next;
4219
4289
  });
4220
- const index = Math.floor(progress / 100 * logs.length);
4221
- setLog(logs[index] || logs[logs.length - 1]);
4222
- }, 30);
4290
+ }, 20);
4223
4291
  return () => clearInterval(timer);
4224
- }, [progress]);
4225
- const width = 60;
4226
- const filled = Math.floor(width * progress / 100);
4227
- const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
4228
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 20, children: [
4229
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(BigText, { text: "AGDI OS", font: "block", align: "center", colors: ["cyan", "blue"] }) }),
4230
- /* @__PURE__ */ jsx(Box, { marginY: 1, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
4231
- bar,
4232
- " ",
4233
- progress,
4234
- "%"
4235
- ] }) }),
4236
- /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
4237
- "\u279C ",
4238
- log
4292
+ }, []);
4293
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 15, children: [
4294
+ /* @__PURE__ */ jsx(Text, { color: THEME2.accent, bold: true, children: "Initializing Agdi Workspace..." }),
4295
+ /* @__PURE__ */ jsxs(Text, { color: THEME2.dim, children: [
4296
+ "\u2588",
4297
+ "\u2588".repeat(progress / 5)
4239
4298
  ] })
4240
4299
  ] });
4241
4300
  };
4242
- var FileExplorer = ({ cwd, lastChange }) => {
4243
- const [files, setFiles] = useState([]);
4244
- useEffect(() => {
4245
- const refresh = () => {
4246
- try {
4247
- const list = fs7.readdirSync(cwd).filter((f) => !f.startsWith(".")).slice(0, 15);
4248
- setFiles(list);
4249
- } catch {
4250
- setFiles(["<Error reading dir>"]);
4251
- }
4252
- };
4253
- refresh();
4254
- const timer = setInterval(refresh, 2e3);
4255
- return () => clearInterval(timer);
4256
- }, [cwd, lastChange]);
4257
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", title: " FILESYSTEM ", width: "30%", children: [
4258
- /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
4259
- " ",
4260
- cwd.length > 30 ? "..." + cwd.slice(-30) : cwd
4261
- ] }),
4262
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
4263
- files.map((f, i) => /* @__PURE__ */ jsxs(Text, { color: "white", children: [
4264
- fs7.statSync(path7.join(cwd, f)).isDirectory() ? "\u{1F4C1} " : "\u{1F4C4} ",
4265
- f
4266
- ] }, i)),
4267
- files.length === 0 && /* @__PURE__ */ jsx(Text, { color: "gray", children: " (empty)" })
4268
- ] })
4301
+ var Sidebar = ({ history }) => {
4302
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: 25, borderStyle: "single", borderColor: THEME2.border, paddingX: 1, children: [
4303
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, borderStyle: "single", borderColor: THEME2.accent, justifyContent: "center", children: /* @__PURE__ */ jsx(Text, { bold: true, children: "+ New task" }) }),
4304
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, bold: true, children: "RECENTS" }),
4305
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 0, children: history.length === 0 ? /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "No recent sessions" }) : history.map((h, i) => /* @__PURE__ */ jsx(Text, { color: THEME2.text, children: h }, i)) })
4269
4306
  ] });
4270
4307
  };
4271
- var LogPanel = ({ logs }) => {
4272
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", title: " AGENT LOGS ", width: "70%", marginLeft: 1, children: [
4273
- logs.slice(-12).map((log, i) => /* @__PURE__ */ jsxs(Text, { color: "green", children: [
4274
- /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
4275
- "[",
4276
- (/* @__PURE__ */ new Date()).toLocaleTimeString(),
4277
- "]"
4278
- ] }),
4279
- " ",
4280
- log
4281
- ] }, i)),
4282
- logs.length === 0 && /* @__PURE__ */ jsx(Text, { color: "gray", children: "Waiting for agent activity..." })
4308
+ var ContextPanel = ({ files, agentStatus }) => {
4309
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: 30, borderStyle: "single", borderColor: THEME2.border, paddingX: 1, children: [
4310
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
4311
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Context" }),
4312
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "WORKING FILES" }),
4313
+ files.length === 0 ? /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "None yet." }) : files.slice(0, 5).map((f, i) => /* @__PURE__ */ jsxs(Text, { color: THEME2.accent, children: [
4314
+ "\u{1F4C4} ",
4315
+ f
4316
+ ] }, i))
4317
+ ] }),
4318
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: THEME2.dim, padding: 0, children: [
4319
+ /* @__PURE__ */ jsx(Text, { bold: true, children: " Plugins" }),
4320
+ /* @__PURE__ */ jsx(Text, { color: THEME2.success, children: "\u2022 Scheduler" }),
4321
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: " Run scheduled jobs" })
4322
+ ] }),
4323
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
4324
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Status" }),
4325
+ /* @__PURE__ */ jsxs(Text, { color: agentStatus === "IDLE" ? THEME2.dim : THEME2.success, children: [
4326
+ "\u2022 ",
4327
+ agentStatus
4328
+ ] })
4329
+ ] })
4283
4330
  ] });
4284
4331
  };
4285
- var ChatPanel = ({ history, onSend, placeholder = "" }) => {
4332
+ var ChatArea = ({ history, onSend, placeholder }) => {
4286
4333
  const [query, setQuery] = useState("");
4287
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", title: " MISSION CONTROL ", height: 12, children: [
4288
- /* @__PURE__ */ jsx(Box, { flexDirection: "column", flexGrow: 1, justifyContent: "flex-end", children: history.slice(-5).map((msg, i) => /* @__PURE__ */ jsxs(Box, { marginY: 0, children: [
4289
- /* @__PURE__ */ jsx(Text, { color: msg.role === "user" ? "cyan" : msg.role === "system" ? "yellow" : "magenta", bold: true, children: msg.role === "user" ? "YOU \u203A " : msg.role === "system" ? "SYS \u203A " : "AGDI \u203A " }),
4290
- /* @__PURE__ */ jsx(Text, { children: msg.text })
4334
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, borderStyle: "single", borderColor: THEME2.border, marginLeft: 0, marginRight: 0, children: [
4335
+ /* @__PURE__ */ jsx(Box, { justifyContent: "center", borderStyle: "single", borderBottomColor: THEME2.border, borderTop: false, borderLeft: false, borderRight: false, paddingBottom: 0, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Start a conversation" }) }),
4336
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", flexGrow: 1, padding: 1, justifyContent: "flex-end", children: history.length === 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 10, children: [
4337
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "Describe what you want to do," }),
4338
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "and Agdi will take it from there." })
4339
+ ] }) : history.slice(-8).map((msg, i) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
4340
+ /* @__PURE__ */ jsx(Text, { bold: true, color: msg.role === "user" ? THEME2.text : THEME2.accent, children: msg.role === "user" ? "You" : "Agdi" }),
4341
+ /* @__PURE__ */ jsx(Text, { color: THEME2.text, children: msg.text })
4291
4342
  ] }, i)) }),
4292
- /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "gray", marginTop: 1, children: [
4293
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u279C " }),
4294
- /* @__PURE__ */ jsx(
4295
- TextInput,
4296
- {
4297
- value: query,
4298
- onChange: setQuery,
4299
- onSubmit: (val) => {
4300
- onSend(val);
4301
- setQuery("");
4302
- },
4303
- placeholder
4304
- }
4305
- )
4306
- ] })
4343
+ /* @__PURE__ */ jsx(Box, { borderStyle: "round", borderColor: THEME2.dim, paddingX: 1, marginX: 1, marginBottom: 1, children: /* @__PURE__ */ jsx(
4344
+ TextInput,
4345
+ {
4346
+ value: query,
4347
+ onChange: setQuery,
4348
+ onSubmit: (val) => {
4349
+ onSend(val);
4350
+ setQuery("");
4351
+ },
4352
+ placeholder
4353
+ }
4354
+ ) })
4307
4355
  ] });
4308
4356
  };
4309
4357
  var Dashboard = () => {
4310
4358
  const { exit } = useApp();
4311
4359
  const [cwd, setCwd] = useState(process.cwd());
4312
- const [logs, setLogs] = useState(["System ready.", "Initializing Neural Link..."]);
4313
4360
  const [chatHistory, setChatHistory] = useState([]);
4314
4361
  const [step, setStep] = useState("safety");
4315
4362
  const [pendingAction, setPendingAction] = useState(null);
4316
- const [lastFileChange, setLastFileChange] = useState(0);
4317
- const [activeAgent, setActiveAgent] = useState("IDLE");
4363
+ const [activeFiles, setActiveFiles] = useState([]);
4364
+ const [agentStatus, setAgentStatus] = useState("IDLE");
4318
4365
  useEffect(() => {
4319
4366
  const handleEvent = (event) => {
4320
4367
  if (event.type === "handoff") {
4321
- setActiveAgent(event.role.toUpperCase());
4368
+ setAgentStatus(event.role.toUpperCase());
4322
4369
  }
4323
- if (event.type === "log" || event.type === "thought") {
4324
- setLogs((prev) => [...prev, `[${event.agentName}] ${event.message}`]);
4370
+ if (event.type === "thought") {
4371
+ if (!event.message.startsWith("Analyzing")) {
4372
+ setChatHistory((prev) => [...prev, { role: "system", text: `[${event.agentName}] ${event.message}` }]);
4373
+ }
4325
4374
  }
4326
- if (event.message.includes("Created:") || event.message.includes("Modified:")) {
4327
- setLastFileChange(Date.now());
4375
+ if (event.message.includes("Created:")) {
4376
+ const filename = event.message.split("Created:")[1].trim();
4377
+ setActiveFiles((prev) => [filename, ...prev].slice(0, 5));
4328
4378
  }
4329
4379
  };
4330
4380
  agentEventBus.on("agent_event", handleEvent);
@@ -4339,13 +4389,12 @@ var Dashboard = () => {
4339
4389
  const isUnsafe = cwd === home || cwd === root;
4340
4390
  if (isUnsafe) {
4341
4391
  setChatHistory([
4342
- { role: "system", text: `\u26A0\uFE0F SAFETY WARNING: You are running in ${cwd}` },
4343
- { role: "ai", text: "It is unsafe to run here. Should I create a new project folder for you? (yes/no)" }
4392
+ { role: "system", text: `\u26A0\uFE0F Safety Check: Running in ${cwd}` },
4393
+ { role: "ai", text: "This directory is too broad. Should I create a new project folder? (yes/no)" }
4344
4394
  ]);
4345
4395
  setPendingAction("safety_confirm");
4346
4396
  } else {
4347
4397
  setStep("prompt");
4348
- setChatHistory([{ role: "ai", text: "Safety check passed. What are we building today?" }]);
4349
4398
  }
4350
4399
  }
4351
4400
  }, []);
@@ -4354,13 +4403,12 @@ var Dashboard = () => {
4354
4403
  setChatHistory((prev) => [...prev, { role: "user", text: cmd }]);
4355
4404
  if (step === "safety" && pendingAction === "safety_confirm") {
4356
4405
  if (cmd.toLowerCase().startsWith("y")) {
4357
- setChatHistory((prev) => [...prev, { role: "ai", text: "Great. Enter a name for your project folder:" }]);
4406
+ setChatHistory((prev) => [...prev, { role: "ai", text: "Name your project:" }]);
4358
4407
  setPendingAction("create_folder");
4359
4408
  } else {
4360
- setChatHistory((prev) => [...prev, { role: "ai", text: "Okay, proceeding in current directory (AT YOUR OWN RISK)." }]);
4409
+ setChatHistory((prev) => [...prev, { role: "ai", text: "Proceeding in current directory." }]);
4361
4410
  setStep("prompt");
4362
4411
  setPendingAction(null);
4363
- setTimeout(() => setChatHistory((prev) => [...prev, { role: "ai", text: "What are we building today?" }]), 500);
4364
4412
  }
4365
4413
  return;
4366
4414
  }
@@ -4370,20 +4418,17 @@ var Dashboard = () => {
4370
4418
  if (!fs7.existsSync(newPath)) fs7.mkdirSync(newPath);
4371
4419
  process.chdir(newPath);
4372
4420
  setCwd(newPath);
4373
- setLogs((prev) => [...prev, `Switched directory to ${newPath}`]);
4374
4421
  setChatHistory((prev) => [...prev, { role: "system", text: `\u{1F4C2} Switched to ${newPath}` }]);
4375
4422
  setStep("prompt");
4376
4423
  setPendingAction(null);
4377
- setTimeout(() => setChatHistory((prev) => [...prev, { role: "ai", text: "What are we building today?" }]), 500);
4378
4424
  } catch (e) {
4379
- setChatHistory((prev) => [...prev, { role: "system", text: `Error creating folder: ${e}` }]);
4425
+ setChatHistory((prev) => [...prev, { role: "system", text: `Error: ${e}` }]);
4380
4426
  }
4381
4427
  return;
4382
4428
  }
4383
4429
  if (step === "prompt") {
4384
- setChatHistory((prev) => [...prev, { role: "ai", text: "Analyzing requirements..." }]);
4385
4430
  setStep("active");
4386
- setActiveAgent("MANAGER");
4431
+ setAgentStatus("MANAGER");
4387
4432
  const activeConfig = getActiveProvider();
4388
4433
  if (!activeConfig) {
4389
4434
  setChatHistory((prev) => [...prev, { role: "system", text: '\u274C No API key found. Run "agdi auth" first.' }]);
@@ -4396,51 +4441,27 @@ var Dashboard = () => {
4396
4441
  runSquadCommand(cmd, llm, {
4397
4442
  output: cwd,
4398
4443
  verbose: false,
4399
- // We handle logs via event bus now
4400
4444
  deploy: false
4401
4445
  }).then(() => {
4402
- setActiveAgent("IDLE");
4403
- setChatHistory((prev) => [...prev, { role: "ai", text: "\u{1F680} Mission Complete! Check the files." }]);
4446
+ setAgentStatus("IDLE");
4447
+ setChatHistory((prev) => [...prev, { role: "ai", text: "Task completed." }]);
4404
4448
  }).catch((err) => {
4405
- setActiveAgent("ERROR");
4406
- setChatHistory((prev) => [...prev, { role: "system", text: `\u274C Error: ${err.message}` }]);
4449
+ setAgentStatus("ERROR");
4450
+ setChatHistory((prev) => [...prev, { role: "system", text: `Error: ${err.message}` }]);
4407
4451
  });
4408
4452
  }
4409
4453
  };
4410
- const getStatusColor = () => {
4411
- switch (activeAgent) {
4412
- case "MANAGER":
4413
- return "magenta";
4414
- case "FRONTEND":
4415
- return "cyan";
4416
- case "BACKEND":
4417
- return "blue";
4418
- case "QA":
4419
- return "yellow";
4420
- case "DEVOPS":
4421
- return "green";
4422
- case "ERROR":
4423
- return "red";
4424
- default:
4425
- return "gray";
4426
- }
4427
- };
4428
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", padding: 1, height: "100%", children: [
4429
- /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", marginBottom: 1, children: [
4430
- /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "AGDI v3.3.2 [ONLINE]" }),
4431
- /* @__PURE__ */ jsxs(Text, { color: getStatusColor(), children: [
4432
- "AGENT: ",
4433
- activeAgent,
4434
- " ",
4435
- activeAgent !== "IDLE" && /* @__PURE__ */ jsx(Spinner, { type: "dots" })
4436
- ] }),
4437
- /* @__PURE__ */ jsx(Text, { color: "gray", children: "NET: CONNECTED" })
4438
- ] }),
4439
- /* @__PURE__ */ jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [
4440
- /* @__PURE__ */ jsx(FileExplorer, { cwd, lastChange: lastFileChange }),
4441
- /* @__PURE__ */ jsx(LogPanel, { logs })
4442
- ] }),
4443
- /* @__PURE__ */ jsx(Box, { marginTop: 0, children: /* @__PURE__ */ jsx(ChatPanel, { history: chatHistory, onSend: handleCommand, placeholder: step === "prompt" ? "Describe your app..." : "" }) })
4454
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", height: 30, padding: 1, children: [
4455
+ /* @__PURE__ */ jsx(Sidebar, { history: ["New session - " + (/* @__PURE__ */ new Date()).toLocaleDateString()] }),
4456
+ /* @__PURE__ */ jsx(
4457
+ ChatArea,
4458
+ {
4459
+ history: chatHistory,
4460
+ onSend: handleCommand,
4461
+ placeholder: step === "prompt" ? "Ask Agdi..." : "Reply..."
4462
+ }
4463
+ ),
4464
+ /* @__PURE__ */ jsx(ContextPanel, { files: activeFiles, agentStatus })
4444
4465
  ] });
4445
4466
  };
4446
4467
  var App = () => {
@@ -4465,7 +4486,7 @@ ${chalk14.cyan(`/_/ |_|\\_, /\\__,_//_/ `)}
4465
4486
  ${chalk14.cyan(` /____/ `)}
4466
4487
  `;
4467
4488
  var program = new Command();
4468
- program.name("agdi").description(chalk14.cyan("\u{1F9B8} The Autonomous AI Employee")).version("3.3.2").option("-y, --yes", "Auto-approve all prompts (headless/CI mode)").option("-m, --minimal", "Generate only the requested file(s), not a full app").option("-d, --dry-run", "Show what would be created without writing files").option("--saas", "Generate a production SaaS blueprint (Next.js + Prisma + Postgres + Stripe)");
4489
+ program.name("agdi").description(chalk14.cyan("\u{1F9B8} The Autonomous AI Employee")).version("3.3.4").option("-y, --yes", "Auto-approve all prompts (headless/CI mode)").option("-m, --minimal", "Generate only the requested file(s), not a full app").option("-d, --dry-run", "Show what would be created without writing files").option("--saas", "Generate a production SaaS blueprint (Next.js + Prisma + Postgres + Stripe)");
4469
4490
  program.hook("preAction", (thisCommand) => {
4470
4491
  const opts = thisCommand.opts();
4471
4492
  if (opts.yes) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agdi",
3
- "version": "3.3.2",
3
+ "version": "3.3.4",
4
4
  "description": "AI-powered app generator - build full-stack apps from natural language in your terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,14 +0,0 @@
1
- // src/core/event-bus.ts
2
- import { EventEmitter } from "events";
3
- var agentEventBus = new EventEmitter();
4
- function emitAgentEvent(event) {
5
- agentEventBus.emit("agent_event", {
6
- ...event,
7
- timestamp: Date.now()
8
- });
9
- }
10
-
11
- export {
12
- agentEventBus,
13
- emitAgentEvent
14
- };