@locusai/sdk 0.4.16 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -439,13 +439,18 @@ __export(exports_worker, {
439
439
  AgentWorker: () => AgentWorker
440
440
  });
441
441
  module.exports = __toCommonJS(exports_worker);
442
-
443
- // src/ai/anthropic-client.ts
444
- var import_sdk = __toESM(require("@anthropic-ai/sdk"));
442
+ var import_shared3 = require("@locusai/shared");
445
443
 
446
444
  // src/core/config.ts
447
445
  var import_node_path = require("node:path");
448
- var DEFAULT_MODEL = "sonnet";
446
+ var PROVIDER = {
447
+ CLAUDE: "claude",
448
+ CODEX: "codex"
449
+ };
450
+ var DEFAULT_MODEL = {
451
+ [PROVIDER.CLAUDE]: "sonnet",
452
+ [PROVIDER.CODEX]: "gpt-5.1-codex-mini"
453
+ };
449
454
  var LOCUS_CONFIG = {
450
455
  dir: ".locus",
451
456
  configFile: "config.json",
@@ -460,122 +465,9 @@ function getLocusPath(projectPath, fileName) {
460
465
  return import_node_path.join(projectPath, LOCUS_CONFIG.dir, LOCUS_CONFIG[fileName]);
461
466
  }
462
467
 
463
- // src/ai/anthropic-client.ts
464
- class AnthropicClient {
465
- client;
466
- model;
467
- constructor(config) {
468
- this.client = new import_sdk.default({
469
- apiKey: config.apiKey
470
- });
471
- this.model = config.model || DEFAULT_MODEL;
472
- }
473
- async run(options) {
474
- const { systemPrompt, cacheableContext = [], userPrompt } = options;
475
- const systemContent = [];
476
- if (systemPrompt) {
477
- systemContent.push({
478
- type: "text",
479
- text: systemPrompt
480
- });
481
- }
482
- for (let i = 0;i < cacheableContext.length; i++) {
483
- const isLast = i === cacheableContext.length - 1;
484
- systemContent.push({
485
- type: "text",
486
- text: cacheableContext[i],
487
- ...isLast && {
488
- cache_control: { type: "ephemeral" }
489
- }
490
- });
491
- }
492
- const response = await this.client.messages.create({
493
- model: this.model,
494
- max_tokens: 8000,
495
- system: systemContent,
496
- messages: [
497
- {
498
- role: "user",
499
- content: userPrompt
500
- }
501
- ]
502
- });
503
- const textBlocks = response.content.filter((block) => block.type === "text");
504
- return textBlocks.map((block) => block.text).join(`
505
- `);
506
- }
507
- async runSimple(prompt) {
508
- return this.run({
509
- userPrompt: prompt
510
- });
511
- }
512
- }
513
-
514
468
  // src/ai/claude-runner.ts
515
469
  var import_node_child_process = require("node:child_process");
516
- class ClaudeRunner {
517
- projectPath;
518
- model;
519
- constructor(projectPath, model = DEFAULT_MODEL) {
520
- this.projectPath = projectPath;
521
- this.model = model;
522
- }
523
- async run(prompt, _isPlanning = false) {
524
- const maxRetries = 3;
525
- let lastError = null;
526
- for (let attempt = 1;attempt <= maxRetries; attempt++) {
527
- try {
528
- return await this.executeRun(prompt);
529
- } catch (error) {
530
- const err = error;
531
- lastError = err;
532
- const isLastAttempt = attempt === maxRetries;
533
- if (!isLastAttempt) {
534
- const delay = Math.pow(2, attempt) * 1000;
535
- console.warn(`Claude CLI attempt ${attempt} failed: ${err.message}. Retrying in ${delay}ms...`);
536
- await new Promise((resolve) => setTimeout(resolve, delay));
537
- }
538
- }
539
- }
540
- throw lastError || new Error("Claude CLI failed after multiple attempts");
541
- }
542
- executeRun(prompt) {
543
- return new Promise((resolve, reject) => {
544
- const args = [
545
- "--dangerously-skip-permissions",
546
- "--print",
547
- "--model",
548
- this.model
549
- ];
550
- const claude = import_node_child_process.spawn("claude", args, {
551
- cwd: this.projectPath,
552
- stdio: ["pipe", "pipe", "pipe"],
553
- env: process.env,
554
- shell: true
555
- });
556
- let output = "";
557
- let errorOutput = "";
558
- claude.stdout.on("data", (data) => {
559
- output += data.toString();
560
- });
561
- claude.stderr.on("data", (data) => {
562
- errorOutput += data.toString();
563
- });
564
- claude.on("error", (err) => reject(new Error(`Failed to start Claude CLI (shell: true): ${err.message}. Please ensure the 'claude' command is available in your PATH.`)));
565
- claude.on("close", (code) => {
566
- if (code === 0)
567
- resolve(output);
568
- else {
569
- const detail = errorOutput.trim();
570
- const message = detail ? `Claude CLI error (exit code ${code}): ${detail}` : `Claude CLI exited with code ${code}. Please ensure the Claude CLI is installed and you are logged in (run 'claude' manually to check).`;
571
- reject(new Error(message));
572
- }
573
- });
574
- claude.stdin.write(prompt);
575
- claude.stdin.end();
576
- });
577
- }
578
- }
470
+ var import_node_path2 = require("node:path");
579
471
 
580
472
  // src/utils/colors.ts
581
473
  var ESC = "\x1B[";
@@ -625,9 +517,270 @@ var c = {
625
517
  underline: (t) => c.text(t, "underline")
626
518
  };
627
519
 
628
- // src/agent/artifact-syncer.ts
520
+ // src/ai/claude-runner.ts
521
+ class ClaudeRunner {
522
+ model;
523
+ log;
524
+ projectPath;
525
+ constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CLAUDE], log) {
526
+ this.model = model;
527
+ this.log = log;
528
+ this.projectPath = import_node_path2.resolve(projectPath);
529
+ }
530
+ async run(prompt, _isPlanning = false) {
531
+ const maxRetries = 3;
532
+ let lastError = null;
533
+ for (let attempt = 1;attempt <= maxRetries; attempt++) {
534
+ try {
535
+ return await this.executeRun(prompt);
536
+ } catch (error) {
537
+ const err = error;
538
+ lastError = err;
539
+ const isLastAttempt = attempt === maxRetries;
540
+ if (!isLastAttempt) {
541
+ const delay = Math.pow(2, attempt) * 1000;
542
+ console.warn(`Claude CLI attempt ${attempt} failed: ${err.message}. Retrying in ${delay}ms...`);
543
+ await new Promise((resolve2) => setTimeout(resolve2, delay));
544
+ }
545
+ }
546
+ }
547
+ throw lastError || new Error("Claude CLI failed after multiple attempts");
548
+ }
549
+ executeRun(prompt) {
550
+ return new Promise((resolve2, reject) => {
551
+ const args = [
552
+ "--dangerously-skip-permissions",
553
+ "--print",
554
+ "--verbose",
555
+ "--output-format",
556
+ "stream-json",
557
+ "--include-partial-messages",
558
+ "--model",
559
+ this.model
560
+ ];
561
+ const env = {
562
+ ...process.env,
563
+ FORCE_COLOR: "1",
564
+ TERM: "xterm-256color"
565
+ };
566
+ const claude = import_node_child_process.spawn("claude", args, {
567
+ cwd: this.projectPath,
568
+ stdio: ["pipe", "pipe", "pipe"],
569
+ env
570
+ });
571
+ let finalResult = "";
572
+ let errorOutput = "";
573
+ let buffer = "";
574
+ claude.stdout.on("data", (data) => {
575
+ buffer += data.toString();
576
+ const lines = buffer.split(`
577
+ `);
578
+ buffer = lines.pop() || "";
579
+ for (const line of lines) {
580
+ const result = this.handleStreamLine(line);
581
+ if (result)
582
+ finalResult = result;
583
+ }
584
+ });
585
+ claude.stderr.on("data", (data) => {
586
+ const msg = data.toString();
587
+ errorOutput += msg;
588
+ process.stderr.write(msg);
589
+ });
590
+ claude.on("error", (err) => {
591
+ reject(new Error(`Failed to start Claude CLI: ${err.message}. Please ensure the 'claude' command is available in your PATH.`));
592
+ });
593
+ claude.on("close", (code) => {
594
+ process.stdout.write(`
595
+ `);
596
+ if (code === 0) {
597
+ resolve2(finalResult);
598
+ } else {
599
+ reject(this.createExecutionError(code, errorOutput));
600
+ }
601
+ });
602
+ claude.stdin.write(prompt);
603
+ claude.stdin.end();
604
+ });
605
+ }
606
+ handleStreamLine(line) {
607
+ if (!line.trim())
608
+ return null;
609
+ try {
610
+ const item = JSON.parse(line);
611
+ return this.processStreamItem(item);
612
+ } catch {
613
+ return null;
614
+ }
615
+ }
616
+ processStreamItem(item) {
617
+ if (item.type === "result") {
618
+ return item.result || "";
619
+ }
620
+ if (item.type === "stream_event" && item.event) {
621
+ this.handleEvent(item.event);
622
+ }
623
+ return null;
624
+ }
625
+ handleEvent(event) {
626
+ const { type, delta, content_block } = event;
627
+ if (type === "content_block_delta" && delta) {
628
+ if (delta.type === "text_delta" && delta.text) {
629
+ this.log?.(delta.text, "info");
630
+ }
631
+ } else if (type === "content_block_start" && content_block) {
632
+ if (content_block.type === "tool_use" && content_block.name) {
633
+ this.log?.(`
634
+
635
+ ${c.primary("[Claude]")} ${c.bold(`Running ${content_block.name}...`)}
636
+ `, "info");
637
+ }
638
+ }
639
+ }
640
+ createExecutionError(code, detail) {
641
+ const errorMsg = detail.trim();
642
+ const message = errorMsg ? `Claude CLI error (exit code ${code}): ${errorMsg}` : `Claude CLI exited with code ${code}. Please ensure the Claude CLI is installed and you are logged in.`;
643
+ return new Error(message);
644
+ }
645
+ }
646
+
647
+ // src/ai/codex-runner.ts
648
+ var import_node_child_process2 = require("node:child_process");
649
+ var import_node_crypto = require("node:crypto");
629
650
  var import_node_fs = require("node:fs");
630
- var import_node_path2 = require("node:path");
651
+ var import_node_os = require("node:os");
652
+ var import_node_path3 = require("node:path");
653
+ class CodexRunner {
654
+ projectPath;
655
+ model;
656
+ log;
657
+ constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CODEX], log) {
658
+ this.projectPath = projectPath;
659
+ this.model = model;
660
+ this.log = log;
661
+ }
662
+ async run(prompt) {
663
+ const maxRetries = 3;
664
+ let lastError = null;
665
+ for (let attempt = 1;attempt <= maxRetries; attempt++) {
666
+ try {
667
+ return await this.executeRun(prompt);
668
+ } catch (error) {
669
+ lastError = error;
670
+ if (attempt < maxRetries) {
671
+ const delay = Math.pow(2, attempt) * 1000;
672
+ console.warn(`Codex CLI attempt ${attempt} failed: ${lastError.message}. Retrying in ${delay}ms...`);
673
+ await this.sleep(delay);
674
+ }
675
+ }
676
+ }
677
+ throw lastError || new Error("Codex CLI failed after multiple attempts");
678
+ }
679
+ executeRun(prompt) {
680
+ return new Promise((resolve2, reject) => {
681
+ const outputPath = import_node_path3.join(import_node_os.tmpdir(), `locus-codex-${import_node_crypto.randomUUID()}.txt`);
682
+ const args = this.buildArgs(outputPath);
683
+ const codex = import_node_child_process2.spawn("codex", args, {
684
+ cwd: this.projectPath,
685
+ stdio: ["pipe", "pipe", "pipe"],
686
+ env: process.env,
687
+ shell: false
688
+ });
689
+ let output = "";
690
+ let errorOutput = "";
691
+ const handleOutput = (data) => {
692
+ const msg = data.toString();
693
+ output += msg;
694
+ this.streamToConsole(msg);
695
+ };
696
+ codex.stdout.on("data", handleOutput);
697
+ codex.stderr.on("data", (data) => {
698
+ const msg = data.toString();
699
+ errorOutput += msg;
700
+ this.streamToConsole(msg);
701
+ });
702
+ codex.on("error", (err) => {
703
+ reject(new Error(`Failed to start Codex CLI: ${err.message}. ` + `Ensure 'codex' is installed and available in PATH.`));
704
+ });
705
+ codex.on("close", (code) => {
706
+ this.cleanupTempFile(outputPath);
707
+ if (code === 0) {
708
+ resolve2(this.readOutput(outputPath, output));
709
+ } else {
710
+ reject(this.createErrorFromOutput(code, errorOutput));
711
+ }
712
+ });
713
+ codex.stdin.write(prompt);
714
+ codex.stdin.end();
715
+ });
716
+ }
717
+ buildArgs(outputPath) {
718
+ const args = ["exec", "--full-auto", "--output-last-message", outputPath];
719
+ if (this.model) {
720
+ args.push("--model", this.model);
721
+ }
722
+ args.push("-");
723
+ return args;
724
+ }
725
+ streamToConsole(chunk) {
726
+ for (const rawLine of chunk.split(`
727
+ `)) {
728
+ const line = rawLine.trim();
729
+ if (line && this.shouldDisplay(line)) {
730
+ const formattedLine = "[Codex]: ".concat(line.replace(/\*/g, ""));
731
+ this.log?.(formattedLine, "info");
732
+ }
733
+ }
734
+ }
735
+ shouldDisplay(line) {
736
+ return [
737
+ /^thinking\b/,
738
+ /^\*\*/,
739
+ /^Plan update\b/,
740
+ /^[→•✓]/
741
+ ].some((pattern) => pattern.test(line));
742
+ }
743
+ readOutput(outputPath, fallback) {
744
+ if (import_node_fs.existsSync(outputPath)) {
745
+ try {
746
+ const text = import_node_fs.readFileSync(outputPath, "utf-8").trim();
747
+ if (text)
748
+ return text;
749
+ } catch {}
750
+ }
751
+ return fallback.trim();
752
+ }
753
+ createErrorFromOutput(code, errorOutput) {
754
+ const detail = errorOutput.trim();
755
+ const message = detail ? `Codex CLI error (exit code ${code}): ${detail}` : `Codex CLI exited with code ${code}. ` + `Ensure Codex CLI is installed and you are logged in.`;
756
+ return new Error(message);
757
+ }
758
+ cleanupTempFile(path) {
759
+ try {
760
+ if (import_node_fs.existsSync(path))
761
+ import_node_fs.unlinkSync(path);
762
+ } catch {}
763
+ }
764
+ sleep(ms) {
765
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
766
+ }
767
+ }
768
+
769
+ // src/ai/factory.ts
770
+ function createAiRunner(provider, config) {
771
+ const resolvedProvider = provider ?? PROVIDER.CLAUDE;
772
+ const model = config.model ?? DEFAULT_MODEL[resolvedProvider];
773
+ switch (resolvedProvider) {
774
+ case PROVIDER.CODEX:
775
+ return new CodexRunner(config.projectPath, model, config.log);
776
+ default:
777
+ return new ClaudeRunner(config.projectPath, model, config.log);
778
+ }
779
+ }
780
+
781
+ // src/agent/artifact-syncer.ts
782
+ var import_node_fs2 = require("node:fs");
783
+ var import_node_path4 = require("node:path");
631
784
  class ArtifactSyncer {
632
785
  deps;
633
786
  constructor(deps) {
@@ -653,21 +806,21 @@ class ArtifactSyncer {
653
806
  }
654
807
  async sync() {
655
808
  const artifactsDir = getLocusPath(this.deps.projectPath, "artifactsDir");
656
- if (!import_node_fs.existsSync(artifactsDir)) {
657
- import_node_fs.mkdirSync(artifactsDir, { recursive: true });
809
+ if (!import_node_fs2.existsSync(artifactsDir)) {
810
+ import_node_fs2.mkdirSync(artifactsDir, { recursive: true });
658
811
  return;
659
812
  }
660
813
  try {
661
- const files = import_node_fs.readdirSync(artifactsDir);
814
+ const files = import_node_fs2.readdirSync(artifactsDir);
662
815
  if (files.length === 0)
663
816
  return;
664
817
  this.deps.log(`Syncing ${files.length} artifacts to server...`, "info");
665
818
  const artifactsGroupId = await this.getOrCreateArtifactsGroup();
666
819
  const existingDocs = await this.deps.client.docs.list(this.deps.workspaceId);
667
820
  for (const file of files) {
668
- const filePath = import_node_path2.join(artifactsDir, file);
669
- if (import_node_fs.statSync(filePath).isFile()) {
670
- const content = import_node_fs.readFileSync(filePath, "utf-8");
821
+ const filePath = import_node_path4.join(artifactsDir, file);
822
+ if (import_node_fs2.statSync(filePath).isFile()) {
823
+ const content = import_node_fs2.readFileSync(filePath, "utf-8");
671
824
  const title = file.replace(/\.md$/, "").trim();
672
825
  if (!title)
673
826
  continue;
@@ -694,28 +847,77 @@ class ArtifactSyncer {
694
847
  }
695
848
 
696
849
  // src/core/indexer.ts
697
- var import_node_fs2 = require("node:fs");
698
- var import_node_path3 = require("node:path");
850
+ var import_node_crypto2 = require("node:crypto");
851
+ var import_node_fs3 = require("node:fs");
852
+ var import_node_path5 = require("node:path");
699
853
  var import_globby = require("globby");
700
854
 
701
855
  class CodebaseIndexer {
702
856
  projectPath;
703
857
  indexPath;
858
+ fullReindexRatioThreshold = 0.2;
704
859
  constructor(projectPath) {
705
860
  this.projectPath = projectPath;
706
- this.indexPath = import_node_path3.join(projectPath, ".locus", "codebase-index.json");
861
+ this.indexPath = import_node_path5.join(projectPath, ".locus", "codebase-index.json");
707
862
  }
708
- async index(onProgress, treeSummarizer) {
863
+ async index(onProgress, treeSummarizer, force = false) {
709
864
  if (!treeSummarizer) {
710
865
  throw new Error("A treeSummarizer is required for this indexing method.");
711
866
  }
712
- if (onProgress)
713
- onProgress("Generating file tree...");
714
- const gitmodulesPath = import_node_path3.join(this.projectPath, ".gitmodules");
867
+ onProgress?.("Generating file tree...");
868
+ const currentFiles = await this.getFileTree();
869
+ const treeString = currentFiles.join(`
870
+ `);
871
+ const newTreeHash = this.hashTree(treeString);
872
+ const existingIndex = this.loadIndex();
873
+ if (!force && existingIndex?.treeHash === newTreeHash) {
874
+ onProgress?.("No file changes detected, skipping reindex");
875
+ return null;
876
+ }
877
+ const currentHashes = this.computeFileHashes(currentFiles);
878
+ const existingHashes = existingIndex?.fileHashes;
879
+ const canIncremental = !force && existingIndex && existingHashes;
880
+ if (canIncremental) {
881
+ onProgress?.("Performing incremental update");
882
+ const { added, deleted, modified } = this.diffFiles(currentHashes, existingHashes);
883
+ const changedFiles = [...added, ...modified];
884
+ const totalChanges = changedFiles.length + deleted.length;
885
+ const existingFileCount = Object.keys(existingHashes).length;
886
+ onProgress?.(`File changes detected: ${changedFiles.length} changed, ${added.length} added, ${deleted.length} deleted`);
887
+ if (existingFileCount > 0) {
888
+ const changeRatio = totalChanges / existingFileCount;
889
+ if (changeRatio <= this.fullReindexRatioThreshold && changedFiles.length > 0) {
890
+ onProgress?.(`Reindexing ${changedFiles.length} changed files and merging with existing index`);
891
+ const incrementalIndex = await treeSummarizer(changedFiles.join(`
892
+ `));
893
+ const updatedIndex = this.cloneIndex(existingIndex);
894
+ this.removeFilesFromIndex(updatedIndex, [...deleted, ...modified]);
895
+ return this.mergeIndex(updatedIndex, incrementalIndex, currentHashes, newTreeHash);
896
+ }
897
+ if (changedFiles.length === 0 && deleted.length > 0) {
898
+ onProgress?.(`Removing ${deleted.length} deleted files from index`);
899
+ const updatedIndex = this.cloneIndex(existingIndex);
900
+ this.removeFilesFromIndex(updatedIndex, deleted);
901
+ return this.applyIndexMetadata(updatedIndex, currentHashes, newTreeHash);
902
+ }
903
+ if (changedFiles.length === 0 && deleted.length === 0) {
904
+ onProgress?.("No actual file changes, updating hashes only");
905
+ const updatedIndex = this.cloneIndex(existingIndex);
906
+ return this.applyIndexMetadata(updatedIndex, currentHashes, newTreeHash);
907
+ }
908
+ onProgress?.(`Too many changes (${(changeRatio * 100).toFixed(1)}%), performing full reindex`);
909
+ }
910
+ }
911
+ onProgress?.("AI is analyzing codebase structure...");
912
+ const index = await treeSummarizer(treeString);
913
+ return this.applyIndexMetadata(index, currentHashes, newTreeHash);
914
+ }
915
+ async getFileTree() {
916
+ const gitmodulesPath = import_node_path5.join(this.projectPath, ".gitmodules");
715
917
  const submoduleIgnores = [];
716
- if (import_node_fs2.existsSync(gitmodulesPath)) {
918
+ if (import_node_fs3.existsSync(gitmodulesPath)) {
717
919
  try {
718
- const content = import_node_fs2.readFileSync(gitmodulesPath, "utf-8");
920
+ const content = import_node_fs3.readFileSync(gitmodulesPath, "utf-8");
719
921
  const lines = content.split(`
720
922
  `);
721
923
  for (const line of lines) {
@@ -728,7 +930,7 @@ class CodebaseIndexer {
728
930
  }
729
931
  } catch {}
730
932
  }
731
- const files = await import_globby.globby(["**/*"], {
933
+ return import_globby.globby(["**/*"], {
732
934
  cwd: this.projectPath,
733
935
  gitignore: true,
734
936
  ignore: [
@@ -769,18 +971,11 @@ class CodebaseIndexer {
769
971
  "**/*.{png,jpg,jpeg,gif,svg,ico,mp4,webm,wav,mp3,woff,woff2,eot,ttf,otf,pdf,zip,tar.gz,rar}"
770
972
  ]
771
973
  });
772
- const treeString = files.join(`
773
- `);
774
- if (onProgress)
775
- onProgress("AI is analyzing codebase structure...");
776
- const index = await treeSummarizer(treeString);
777
- index.lastIndexed = new Date().toISOString();
778
- return index;
779
974
  }
780
975
  loadIndex() {
781
- if (import_node_fs2.existsSync(this.indexPath)) {
976
+ if (import_node_fs3.existsSync(this.indexPath)) {
782
977
  try {
783
- return JSON.parse(import_node_fs2.readFileSync(this.indexPath, "utf-8"));
978
+ return JSON.parse(import_node_fs3.readFileSync(this.indexPath, "utf-8"));
784
979
  } catch {
785
980
  return null;
786
981
  }
@@ -788,11 +983,84 @@ class CodebaseIndexer {
788
983
  return null;
789
984
  }
790
985
  saveIndex(index) {
791
- const dir = import_node_path3.dirname(this.indexPath);
792
- if (!import_node_fs2.existsSync(dir)) {
793
- import_node_fs2.mkdirSync(dir, { recursive: true });
986
+ const dir = import_node_path5.dirname(this.indexPath);
987
+ if (!import_node_fs3.existsSync(dir)) {
988
+ import_node_fs3.mkdirSync(dir, { recursive: true });
989
+ }
990
+ import_node_fs3.writeFileSync(this.indexPath, JSON.stringify(index, null, 2));
991
+ }
992
+ cloneIndex(index) {
993
+ return JSON.parse(JSON.stringify(index));
994
+ }
995
+ applyIndexMetadata(index, fileHashes, treeHash) {
996
+ index.lastIndexed = new Date().toISOString();
997
+ index.treeHash = treeHash;
998
+ index.fileHashes = fileHashes;
999
+ return index;
1000
+ }
1001
+ hashTree(tree) {
1002
+ return import_node_crypto2.createHash("sha256").update(tree).digest("hex");
1003
+ }
1004
+ hashFile(filePath) {
1005
+ try {
1006
+ const content = import_node_fs3.readFileSync(import_node_path5.join(this.projectPath, filePath), "utf-8");
1007
+ return import_node_crypto2.createHash("sha256").update(content).digest("hex").slice(0, 16);
1008
+ } catch {
1009
+ return null;
794
1010
  }
795
- import_node_fs2.writeFileSync(this.indexPath, JSON.stringify(index, null, 2));
1011
+ }
1012
+ computeFileHashes(files) {
1013
+ const hashes = {};
1014
+ for (const file of files) {
1015
+ const hash = this.hashFile(file);
1016
+ if (hash !== null) {
1017
+ hashes[file] = hash;
1018
+ }
1019
+ }
1020
+ return hashes;
1021
+ }
1022
+ diffFiles(currentHashes, existingHashes) {
1023
+ const currentFiles = Object.keys(currentHashes);
1024
+ const existingFiles = Object.keys(existingHashes);
1025
+ const existingSet = new Set(existingFiles);
1026
+ const currentSet = new Set(currentFiles);
1027
+ const added = currentFiles.filter((f) => !existingSet.has(f));
1028
+ const deleted = existingFiles.filter((f) => !currentSet.has(f));
1029
+ const modified = currentFiles.filter((f) => existingSet.has(f) && currentHashes[f] !== existingHashes[f]);
1030
+ return { added, deleted, modified };
1031
+ }
1032
+ removeFilesFromIndex(index, files) {
1033
+ const fileSet = new Set(files);
1034
+ for (const file of files) {
1035
+ delete index.responsibilities[file];
1036
+ }
1037
+ for (const [symbol, paths] of Object.entries(index.symbols)) {
1038
+ index.symbols[symbol] = paths.filter((p) => !fileSet.has(p));
1039
+ if (index.symbols[symbol].length === 0) {
1040
+ delete index.symbols[symbol];
1041
+ }
1042
+ }
1043
+ }
1044
+ mergeIndex(existing, incremental, newHashes, newTreeHash) {
1045
+ const mergedSymbols = { ...existing.symbols };
1046
+ for (const [symbol, paths] of Object.entries(incremental.symbols)) {
1047
+ if (mergedSymbols[symbol]) {
1048
+ mergedSymbols[symbol] = [
1049
+ ...new Set([...mergedSymbols[symbol], ...paths])
1050
+ ];
1051
+ } else {
1052
+ mergedSymbols[symbol] = paths;
1053
+ }
1054
+ }
1055
+ const merged = {
1056
+ symbols: mergedSymbols,
1057
+ responsibilities: {
1058
+ ...existing.responsibilities,
1059
+ ...incremental.responsibilities
1060
+ },
1061
+ lastIndexed: ""
1062
+ };
1063
+ return this.applyIndexMetadata(merged, newHashes, newTreeHash);
796
1064
  }
797
1065
  }
798
1066
 
@@ -804,9 +1072,8 @@ class CodebaseIndexerService {
804
1072
  this.deps = deps;
805
1073
  this.indexer = new CodebaseIndexer(deps.projectPath);
806
1074
  }
807
- async reindex() {
1075
+ async reindex(force = false) {
808
1076
  try {
809
- this.deps.log("Reindexing codebase...", "info");
810
1077
  const index = await this.indexer.index((msg) => this.deps.log(msg, "info"), async (tree) => {
811
1078
  const prompt = `You are a codebase analysis expert. Analyze the file tree and extract:
812
1079
  1. Key symbols (classes, functions, types) and their locations
@@ -821,21 +1088,17 @@ File tree:
821
1088
  ${tree}
822
1089
 
823
1090
  Return ONLY valid JSON, no markdown formatting.`;
824
- let response;
825
- if (this.deps.anthropicClient) {
826
- response = await this.deps.anthropicClient.run({
827
- systemPrompt: "You are a codebase analysis expert specialized in extracting structure and symbols from file trees.",
828
- userPrompt: prompt
829
- });
830
- } else {
831
- response = await this.deps.claudeRunner.run(prompt, true);
832
- }
1091
+ const response = await this.deps.aiRunner.run(prompt, true);
833
1092
  const jsonMatch = response.match(/\{[\s\S]*\}/);
834
1093
  if (jsonMatch) {
835
1094
  return JSON.parse(jsonMatch[0]);
836
1095
  }
837
1096
  return { symbols: {}, responsibilities: {}, lastIndexed: "" };
838
- });
1097
+ }, force);
1098
+ if (index === null) {
1099
+ this.deps.log("No changes detected, skipping reindex", "info");
1100
+ return;
1101
+ }
839
1102
  this.indexer.saveIndex(index);
840
1103
  this.deps.log("Codebase reindexed successfully", "success");
841
1104
  } catch (error) {
@@ -855,30 +1118,7 @@ class SprintPlanner {
855
1118
  try {
856
1119
  const taskList = tasks2.map((t) => `- [${t.id}] ${t.title}: ${t.description || "No description"}`).join(`
857
1120
  `);
858
- let plan;
859
- if (this.deps.anthropicClient) {
860
- const systemPrompt = `You are an expert project manager and lead engineer specialized in sprint planning and task prioritization.`;
861
- const userPrompt = `# Sprint Planning: ${sprint.name}
862
-
863
- ## Tasks
864
- ${taskList}
865
-
866
- ## Instructions
867
- 1. Analyze dependencies between these tasks.
868
- 2. Prioritize them for the most efficient execution.
869
- 3. Create a mindmap (in Markdown or Mermaid format) that visualizes the sprint structure.
870
- 4. Output your final plan. The plan should clearly state the order of execution.
871
-
872
- **IMPORTANT**:
873
- - Do NOT create any files on the filesystem during this planning phase.
874
- - Avoid using absolute local paths (e.g., /Users/...) in your output. Use relative paths starting from the project root if necessary.
875
- - Your output will be saved as the official sprint mindmap on the server.`;
876
- plan = await this.deps.anthropicClient.run({
877
- systemPrompt,
878
- userPrompt
879
- });
880
- } else {
881
- const planningPrompt = `# Sprint Planning: ${sprint.name}
1121
+ const planningPrompt = `# Sprint Planning: ${sprint.name}
882
1122
 
883
1123
  You are an expert project manager and lead engineer. You need to create a mindmap and execution plan for the following tasks in this sprint.
884
1124
 
@@ -895,8 +1135,7 @@ ${taskList}
895
1135
  - Do NOT create any files on the filesystem during this planning phase.
896
1136
  - Avoid using absolute local paths (e.g., /Users/...) in your output. Use relative paths starting from the project root if necessary.
897
1137
  - Your output will be saved as the official sprint mindmap on the server.`;
898
- plan = await this.deps.claudeRunner.run(planningPrompt, true);
899
- }
1138
+ const plan = await this.deps.aiRunner.run(planningPrompt, true);
900
1139
  this.deps.log("Sprint mindmap generated and posted to server.", "success");
901
1140
  return plan;
902
1141
  } catch (error) {
@@ -907,7 +1146,7 @@ ${taskList}
907
1146
  }
908
1147
 
909
1148
  // src/core/prompt-builder.ts
910
- var import_node_fs3 = require("node:fs");
1149
+ var import_node_fs4 = require("node:fs");
911
1150
  var import_shared2 = require("@locusai/shared");
912
1151
  class PromptBuilder {
913
1152
  projectPath;
@@ -930,9 +1169,9 @@ ${task.description || "No description provided."}
930
1169
 
931
1170
  `;
932
1171
  const contextPath = getLocusPath(this.projectPath, "contextFile");
933
- if (import_node_fs3.existsSync(contextPath)) {
1172
+ if (import_node_fs4.existsSync(contextPath)) {
934
1173
  try {
935
- const context = import_node_fs3.readFileSync(contextPath, "utf-8");
1174
+ const context = import_node_fs4.readFileSync(contextPath, "utf-8");
936
1175
  prompt += `## Project Context (from CLAUDE.md)
937
1176
  ${context}
938
1177
 
@@ -942,7 +1181,7 @@ ${context}
942
1181
  }
943
1182
  }
944
1183
  const indexPath = getLocusPath(this.projectPath, "indexFile");
945
- if (import_node_fs3.existsSync(indexPath)) {
1184
+ if (import_node_fs4.existsSync(indexPath)) {
946
1185
  prompt += `## Codebase Overview
947
1186
  There is an index file in the .locus/codebase-index.json and if you need you can check it.
948
1187
 
@@ -1033,22 +1272,20 @@ ${this.deps.sprintPlan}
1033
1272
  ${basePrompt}`;
1034
1273
  }
1035
1274
  try {
1036
- let plan = "";
1037
- if (this.deps.anthropicClient) {
1038
- this.deps.log("Phase 1: Planning (Anthropic SDK)...", "info");
1039
- const cacheableContext = [basePrompt];
1040
- plan = await this.deps.anthropicClient.run({
1041
- systemPrompt: "You are an expert software engineer. Analyze the task carefully and create a detailed implementation plan.",
1042
- cacheableContext,
1043
- userPrompt: `## Phase 1: Planning
1044
- Analyze and create a detailed plan for THIS SPECIFIC TASK. Do NOT execute changes yet.`
1045
- });
1275
+ let plan = null;
1276
+ if (this.deps.skipPlanning) {
1277
+ this.deps.log("Skipping Phase 1: Planning (CLI)...", "info");
1046
1278
  } else {
1047
- this.deps.log("Skipping Phase 1: Planning (No Anthropic API Key)...", "info");
1279
+ this.deps.log("Phase 1: Planning (CLI)...", "info");
1280
+ const planningPrompt = `${basePrompt}
1281
+
1282
+ ## Phase 1: Planning
1283
+ Analyze and create a detailed plan for THIS SPECIFIC TASK. Do NOT execute changes yet.`;
1284
+ plan = await this.deps.aiRunner.run(planningPrompt, true);
1048
1285
  }
1049
1286
  this.deps.log("Starting Execution...", "info");
1050
1287
  let executionPrompt = basePrompt;
1051
- if (plan) {
1288
+ if (plan != null) {
1052
1289
  executionPrompt += `
1053
1290
 
1054
1291
  ## Phase 2: Execution
@@ -1064,11 +1301,11 @@ Execute the task directly.`;
1064
1301
  executionPrompt += `
1065
1302
 
1066
1303
  When finished, output: <promise>COMPLETE</promise>`;
1067
- const output = await this.deps.claudeRunner.run(executionPrompt);
1304
+ const output = await this.deps.aiRunner.run(executionPrompt);
1068
1305
  const success = output.includes("<promise>COMPLETE</promise>");
1069
1306
  return {
1070
1307
  success,
1071
- summary: success ? "Task completed by Claude" : "Claude did not signal completion"
1308
+ summary: success ? "Task completed by the agent" : "The agent did not signal completion"
1072
1309
  };
1073
1310
  } catch (error) {
1074
1311
  return { success: false, summary: `Error: ${error}` };
@@ -1077,17 +1314,27 @@ When finished, output: <promise>COMPLETE</promise>`;
1077
1314
  }
1078
1315
 
1079
1316
  // src/agent/worker.ts
1317
+ function resolveProvider(value) {
1318
+ if (!value || value.startsWith("--")) {
1319
+ console.warn("Warning: --provider requires a value. Falling back to 'claude'.");
1320
+ return PROVIDER.CLAUDE;
1321
+ }
1322
+ if (value === PROVIDER.CLAUDE || value === PROVIDER.CODEX)
1323
+ return value;
1324
+ console.warn(`Warning: invalid --provider value '${value}'. Falling back to 'claude'.`);
1325
+ return PROVIDER.CLAUDE;
1326
+ }
1327
+
1080
1328
  class AgentWorker {
1081
1329
  config;
1082
1330
  client;
1083
- claudeRunner;
1084
- anthropicClient;
1331
+ aiRunner;
1085
1332
  sprintPlanner;
1086
1333
  indexerService;
1087
1334
  artifactSyncer;
1088
1335
  taskExecutor;
1089
1336
  consecutiveEmpty = 0;
1090
- maxEmpty = 10;
1337
+ maxEmpty = 5;
1091
1338
  maxTasks = 50;
1092
1339
  tasksCompleted = 0;
1093
1340
  pollInterval = 1e4;
@@ -1105,41 +1352,37 @@ class AgentWorker {
1105
1352
  factor: 2
1106
1353
  }
1107
1354
  });
1108
- this.claudeRunner = new ClaudeRunner(projectPath, config.model);
1109
- this.anthropicClient = config.anthropicApiKey ? new AnthropicClient({
1110
- apiKey: config.anthropicApiKey,
1111
- model: config.model
1112
- }) : null;
1113
- const logFn = this.log.bind(this);
1355
+ const log = this.log.bind(this);
1356
+ const provider = config.provider ?? PROVIDER.CLAUDE;
1357
+ this.aiRunner = createAiRunner(provider, {
1358
+ projectPath,
1359
+ model: config.model,
1360
+ log
1361
+ });
1114
1362
  this.sprintPlanner = new SprintPlanner({
1115
- anthropicClient: this.anthropicClient,
1116
- claudeRunner: this.claudeRunner,
1117
- log: logFn
1363
+ aiRunner: this.aiRunner,
1364
+ log
1118
1365
  });
1119
1366
  this.indexerService = new CodebaseIndexerService({
1120
- anthropicClient: this.anthropicClient,
1121
- claudeRunner: this.claudeRunner,
1367
+ aiRunner: this.aiRunner,
1122
1368
  projectPath,
1123
- log: logFn
1369
+ log
1124
1370
  });
1125
1371
  this.artifactSyncer = new ArtifactSyncer({
1126
1372
  client: this.client,
1127
1373
  workspaceId: config.workspaceId,
1128
1374
  projectPath,
1129
- log: logFn
1375
+ log
1130
1376
  });
1131
1377
  this.taskExecutor = new TaskExecutor({
1132
- anthropicClient: this.anthropicClient,
1133
- claudeRunner: this.claudeRunner,
1378
+ aiRunner: this.aiRunner,
1134
1379
  projectPath,
1135
1380
  sprintPlan: null,
1136
- log: logFn
1381
+ skipPlanning: config.skipPlanning,
1382
+ log
1137
1383
  });
1138
- if (this.anthropicClient) {
1139
- this.log("Using Anthropic SDK with prompt caching for planning phases", "info");
1140
- } else {
1141
- this.log("Using Claude CLI for all phases", "info");
1142
- }
1384
+ const providerLabel = provider === "codex" ? "Codex" : "Claude";
1385
+ this.log(`Using ${providerLabel} CLI for all phases`, "info");
1143
1386
  }
1144
1387
  log(message, level = "info") {
1145
1388
  const timestamp = new Date().toISOString().split("T")[1]?.slice(0, 8) ?? "";
@@ -1186,16 +1429,17 @@ class AgentWorker {
1186
1429
  const tasks2 = await this.client.tasks.list(this.config.workspaceId, {
1187
1430
  sprintId: sprint.id
1188
1431
  });
1189
- this.log(`Sprint tasks found: ${tasks2.length}`, "info");
1190
- const latestTaskCreation = tasks2.reduce((latest, task) => {
1191
- const taskDate = new Date(task.createdAt);
1192
- return taskDate > latest ? taskDate : latest;
1193
- }, new Date(0));
1194
- const mindmapDate = sprint.mindmapUpdatedAt ? new Date(sprint.mindmapUpdatedAt) : new Date(0);
1195
- if (tasks2.length <= 1) {
1432
+ const activeTasks = tasks2.filter((t) => t.status === import_shared3.TaskStatus.BACKLOG || t.status === import_shared3.TaskStatus.IN_PROGRESS);
1433
+ this.log(`Sprint tasks found: ${activeTasks.length}`, "info");
1434
+ if (activeTasks.length <= 1) {
1196
1435
  this.log("Skipping mindmap generation (only one task in sprint).", "info");
1197
1436
  this.sprintPlan = null;
1198
1437
  } else {
1438
+ const latestTaskCreation = activeTasks.reduce((latest, task) => {
1439
+ const taskDate = new Date(task.createdAt);
1440
+ return taskDate > latest ? taskDate : latest;
1441
+ }, new Date(0));
1442
+ const mindmapDate = sprint.mindmapUpdatedAt ? new Date(sprint.mindmapUpdatedAt) : new Date(0);
1199
1443
  const needsPlanning = !sprint.mindmap || sprint.mindmap.trim() === "" || latestTaskCreation > mindmapDate;
1200
1444
  if (needsPlanning) {
1201
1445
  if (sprint.mindmap && latestTaskCreation > mindmapDate) {
@@ -1217,6 +1461,9 @@ class AgentWorker {
1217
1461
  while (this.tasksCompleted < this.maxTasks && this.consecutiveEmpty < this.maxEmpty) {
1218
1462
  const task = await this.getNextTask();
1219
1463
  if (!task) {
1464
+ if (this.consecutiveEmpty === 0) {
1465
+ this.log("Queue empty, waiting for tasks...", "info");
1466
+ }
1220
1467
  this.consecutiveEmpty++;
1221
1468
  if (this.consecutiveEmpty >= this.maxEmpty)
1222
1469
  break;
@@ -1228,6 +1475,7 @@ class AgentWorker {
1228
1475
  const result = await this.executeTask(task);
1229
1476
  await this.artifactSyncer.sync();
1230
1477
  if (result.success) {
1478
+ this.log(`Completed: ${task.title}`, "success");
1231
1479
  await this.client.tasks.update(task.id, this.config.workspaceId, {
1232
1480
  status: "VERIFICATION"
1233
1481
  });
@@ -1237,6 +1485,7 @@ class AgentWorker {
1237
1485
  });
1238
1486
  this.tasksCompleted++;
1239
1487
  } else {
1488
+ this.log(`Failed: ${task.title} - ${result.summary}`, "error");
1240
1489
  await this.client.tasks.update(task.id, this.config.workspaceId, {
1241
1490
  status: "BACKLOG",
1242
1491
  assignedTo: null
@@ -1265,12 +1514,17 @@ if (process.argv[1]?.includes("agent-worker") || process.argv[1]?.includes("work
1265
1514
  config.apiBase = args[++i];
1266
1515
  else if (arg === "--api-key")
1267
1516
  config.apiKey = args[++i];
1268
- else if (arg === "--anthropic-api-key")
1269
- config.anthropicApiKey = args[++i];
1270
1517
  else if (arg === "--project-path")
1271
1518
  config.projectPath = args[++i];
1272
1519
  else if (arg === "--model")
1273
1520
  config.model = args[++i];
1521
+ else if (arg === "--provider") {
1522
+ const value = args[i + 1];
1523
+ if (value && !value.startsWith("--"))
1524
+ i++;
1525
+ config.provider = resolveProvider(value);
1526
+ } else if (arg === "--skip-planning")
1527
+ config.skipPlanning = true;
1274
1528
  }
1275
1529
  if (!config.agentId || !config.workspaceId || !config.apiBase || !config.apiKey || !config.projectPath) {
1276
1530
  console.error("Missing required arguments");
@@ -1287,6 +1541,7 @@ if (process.argv[1]?.includes("agent-worker") || process.argv[1]?.includes("work
1287
1541
  var exports_index_node = {};
1288
1542
  __export(exports_index_node, {
1289
1543
  getLocusPath: () => getLocusPath,
1544
+ createAiRunner: () => createAiRunner,
1290
1545
  c: () => c,
1291
1546
  WorkspacesModule: () => WorkspacesModule,
1292
1547
  TasksModule: () => TasksModule,
@@ -1294,6 +1549,7 @@ __export(exports_index_node, {
1294
1549
  SprintsModule: () => SprintsModule,
1295
1550
  SprintPlanner: () => SprintPlanner,
1296
1551
  PromptBuilder: () => PromptBuilder,
1552
+ PROVIDER: () => PROVIDER,
1297
1553
  OrganizationsModule: () => OrganizationsModule,
1298
1554
  LocusEvent: () => LocusEvent,
1299
1555
  LocusEmitter: () => LocusEmitter,
@@ -1302,23 +1558,23 @@ __export(exports_index_node, {
1302
1558
  InvitationsModule: () => InvitationsModule,
1303
1559
  DocsModule: () => DocsModule,
1304
1560
  DEFAULT_MODEL: () => DEFAULT_MODEL,
1561
+ CodexRunner: () => CodexRunner,
1305
1562
  CodebaseIndexerService: () => CodebaseIndexerService,
1306
1563
  CodebaseIndexer: () => CodebaseIndexer,
1307
1564
  ClaudeRunner: () => ClaudeRunner,
1308
1565
  CiModule: () => CiModule,
1309
1566
  AuthModule: () => AuthModule,
1310
1567
  ArtifactSyncer: () => ArtifactSyncer,
1311
- AnthropicClient: () => AnthropicClient,
1312
1568
  AgentWorker: () => AgentWorker,
1313
1569
  AgentOrchestrator: () => AgentOrchestrator
1314
1570
  });
1315
1571
  module.exports = __toCommonJS(exports_index_node);
1316
1572
  // src/orchestrator.ts
1317
- var import_node_child_process2 = require("node:child_process");
1318
- var import_node_fs4 = require("node:fs");
1319
- var import_node_path4 = require("node:path");
1573
+ var import_node_child_process3 = require("node:child_process");
1574
+ var import_node_fs5 = require("node:fs");
1575
+ var import_node_path6 = require("node:path");
1320
1576
  var import_node_url = require("node:url");
1321
- var import_shared3 = require("@locusai/shared");
1577
+ var import_shared4 = require("@locusai/shared");
1322
1578
  var import_events3 = require("events");
1323
1579
  class AgentOrchestrator extends import_events3.EventEmitter {
1324
1580
  client;
@@ -1412,9 +1668,9 @@ ${c.success("✅ Orchestrator finished")}`);
1412
1668
  `);
1413
1669
  const potentialPaths = [];
1414
1670
  const currentModulePath = import_node_url.fileURLToPath("file:///home/runner/work/locusai/locusai/packages/sdk/src/orchestrator.ts");
1415
- const currentModuleDir = import_node_path4.dirname(currentModulePath);
1416
- potentialPaths.push(import_node_path4.join(currentModuleDir, "agent", "worker.js"), import_node_path4.join(currentModuleDir, "worker.js"), import_node_path4.join(currentModuleDir, "agent", "worker.ts"));
1417
- const workerPath = potentialPaths.find((p) => import_node_fs4.existsSync(p));
1671
+ const currentModuleDir = import_node_path6.dirname(currentModulePath);
1672
+ potentialPaths.push(import_node_path6.join(currentModuleDir, "agent", "worker.js"), import_node_path6.join(currentModuleDir, "worker.js"), import_node_path6.join(currentModuleDir, "agent", "worker.ts"));
1673
+ const workerPath = potentialPaths.find((p) => import_node_fs5.existsSync(p));
1418
1674
  if (!workerPath) {
1419
1675
  throw new Error(`Worker file not found. Checked: ${potentialPaths.join(", ")}. ` + `Make sure the SDK is properly built and installed.`);
1420
1676
  }
@@ -1430,16 +1686,26 @@ ${c.success("✅ Orchestrator finished")}`);
1430
1686
  "--project-path",
1431
1687
  this.config.projectPath
1432
1688
  ];
1433
- if (this.config.anthropicApiKey) {
1434
- workerArgs.push("--anthropic-api-key", this.config.anthropicApiKey);
1435
- }
1436
1689
  if (this.config.model) {
1437
1690
  workerArgs.push("--model", this.config.model);
1438
1691
  }
1692
+ if (this.config.provider) {
1693
+ workerArgs.push("--provider", this.config.provider);
1694
+ }
1695
+ if (this.config.skipPlanning) {
1696
+ workerArgs.push("--skip-planning");
1697
+ }
1439
1698
  if (this.resolvedSprintId) {
1440
1699
  workerArgs.push("--sprint-id", this.resolvedSprintId);
1441
1700
  }
1442
- const agentProcess = import_node_child_process2.spawn(process.execPath, [workerPath, ...workerArgs]);
1701
+ const agentProcess = import_node_child_process3.spawn(process.execPath, [workerPath, ...workerArgs], {
1702
+ stdio: ["pipe", "pipe", "pipe"],
1703
+ env: {
1704
+ ...process.env,
1705
+ FORCE_COLOR: "1",
1706
+ TERM: "xterm-256color"
1707
+ }
1708
+ });
1443
1709
  agentState.process = agentProcess;
1444
1710
  agentProcess.on("message", (msg) => {
1445
1711
  if (msg.type === "stats") {
@@ -1451,7 +1717,7 @@ ${c.success("✅ Orchestrator finished")}`);
1451
1717
  process.stdout.write(data.toString());
1452
1718
  });
1453
1719
  agentProcess.stderr?.on("data", (data) => {
1454
- process.stderr.write(`[${agentId}] ERR: ${data.toString()}`);
1720
+ process.stderr.write(data.toString());
1455
1721
  });
1456
1722
  agentProcess.on("exit", (code) => {
1457
1723
  console.log(`
@@ -1487,10 +1753,10 @@ ${agentId} finished (exit code: ${code})`);
1487
1753
  try {
1488
1754
  const tasks2 = await this.getAvailableTasks();
1489
1755
  const priorityOrder = [
1490
- import_shared3.TaskPriority.CRITICAL,
1491
- import_shared3.TaskPriority.HIGH,
1492
- import_shared3.TaskPriority.MEDIUM,
1493
- import_shared3.TaskPriority.LOW
1756
+ import_shared4.TaskPriority.CRITICAL,
1757
+ import_shared4.TaskPriority.HIGH,
1758
+ import_shared4.TaskPriority.MEDIUM,
1759
+ import_shared4.TaskPriority.LOW
1494
1760
  ];
1495
1761
  let task = tasks2.sort((a, b) => priorityOrder.indexOf(a.priority) - priorityOrder.indexOf(b.priority))[0];
1496
1762
  if (!task && tasks2.length > 0) {
@@ -1514,7 +1780,7 @@ ${agentId} finished (exit code: ${code})`);
1514
1780
  async completeTask(taskId, agentId, summary) {
1515
1781
  try {
1516
1782
  await this.client.tasks.update(taskId, this.config.workspaceId, {
1517
- status: import_shared3.TaskStatus.VERIFICATION
1783
+ status: import_shared4.TaskStatus.VERIFICATION
1518
1784
  });
1519
1785
  if (summary) {
1520
1786
  await this.client.tasks.addComment(taskId, this.config.workspaceId, {
@@ -1539,7 +1805,7 @@ ${summary}`
1539
1805
  async failTask(taskId, agentId, error) {
1540
1806
  try {
1541
1807
  await this.client.tasks.update(taskId, this.config.workspaceId, {
1542
- status: import_shared3.TaskStatus.BACKLOG,
1808
+ status: import_shared4.TaskStatus.BACKLOG,
1543
1809
  assignedTo: null
1544
1810
  });
1545
1811
  await this.client.tasks.addComment(taskId, this.config.workspaceId, {
@@ -1580,6 +1846,6 @@ ${summary}`
1580
1846
  };
1581
1847
  }
1582
1848
  sleep(ms) {
1583
- return new Promise((resolve) => setTimeout(resolve, ms));
1849
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
1584
1850
  }
1585
1851
  }