heyio 4.2.2 → 4.2.3

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.
@@ -79,7 +79,7 @@ var init_constants = __esm({
79
79
  "packages/shared/dist/constants.js"() {
80
80
  "use strict";
81
81
  APP_NAME = "io";
82
- APP_VERSION = "4.2.2";
82
+ APP_VERSION = "4.2.3";
83
83
  API_PORT = 7777;
84
84
  API_HOST = "0.0.0.0";
85
85
  DEFAULT_MODEL = "gpt-4o";
@@ -91,6 +91,7 @@ var init_constants = __esm({
91
91
  SQUAD_CREATED: "squad.created",
92
92
  SQUAD_DELETED: "squad.deleted",
93
93
  SQUAD_UPDATED: "squad.updated",
94
+ SQUAD_MEMBER_UPDATED: "squad.member_updated",
94
95
  INSTANCE_CREATED: "instance.created",
95
96
  INSTANCE_STARTED: "instance.started",
96
97
  INSTANCE_COMPLETED: "instance.completed",
@@ -77443,6 +77444,10 @@ async function updateMember(memberId, data, db) {
77443
77444
  const database = db ?? await getDatabase();
77444
77445
  const sets = [];
77445
77446
  const args = [];
77447
+ if (data.role !== void 0) {
77448
+ sets.push("role = ?");
77449
+ args.push(data.role);
77450
+ }
77446
77451
  if (data.systemPrompt !== void 0) {
77447
77452
  sets.push("system_prompt = ?");
77448
77453
  args.push(data.systemPrompt);
@@ -77857,8 +77862,8 @@ async function recordUsage(data, db) {
77857
77862
  createdAt: data.createdAt ?? nowIso()
77858
77863
  };
77859
77864
  await database.execute({
77860
- sql: `INSERT INTO token_usage (id, squad_id, agent_id, model, input_tokens, output_tokens, cost, premium_request_cost, token_unit_cost, squad_name, agent_name, created_at)
77861
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
77865
+ sql: `INSERT INTO token_usage (id, squad_id, agent_id, model, input_tokens, output_tokens, cost, squad_name, agent_name, created_at)
77866
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
77862
77867
  args: [
77863
77868
  usage.id,
77864
77869
  usage.squadId,
@@ -77867,8 +77872,6 @@ async function recordUsage(data, db) {
77867
77872
  usage.inputTokens,
77868
77873
  usage.outputTokens,
77869
77874
  usage.cost,
77870
- data.premiumRequestCost ?? null,
77871
- data.tokenUnitCost ?? null,
77872
77875
  data.squadName ?? null,
77873
77876
  data.agentName ?? null,
77874
77877
  usage.createdAt
@@ -79744,11 +79747,16 @@ router7.put("/api/squads/:id/members/:memberId", async (req, res) => {
79744
79747
  res.status(404).json({ error: "Member not found" });
79745
79748
  return;
79746
79749
  }
79747
- const { systemPrompt, model } = req.body;
79750
+ const { role, systemPrompt, model } = req.body;
79748
79751
  const updated = await updateMember(member.id, {
79752
+ role,
79749
79753
  systemPrompt,
79750
79754
  model: model === "" ? null : model
79751
79755
  });
79756
+ eventBus.emit(EVENT_NAMES.SQUAD_MEMBER_UPDATED, {
79757
+ squadId: member.squadId,
79758
+ member: updated
79759
+ });
79752
79760
  res.status(200).json(updated);
79753
79761
  } catch (error51) {
79754
79762
  res.status(500).json({
@@ -81152,10 +81160,19 @@ async function getModelPricing(modelId) {
81152
81160
  sql: "SELECT * FROM model_pricing WHERE id = ?",
81153
81161
  args: [modelId]
81154
81162
  });
81155
- if (result.rows.length === 0) {
81156
- return null;
81163
+ if (result.rows.length > 0) {
81164
+ return rowToModelPricing(result.rows[0]);
81157
81165
  }
81158
- return rowToModelPricing(result.rows[0]);
81166
+ const allModels = await db.execute(
81167
+ "SELECT * FROM model_pricing WHERE token_input_multiplier IS NOT NULL ORDER BY length(id) DESC"
81168
+ );
81169
+ for (const row of allModels.rows) {
81170
+ const storedId = asString(row.id);
81171
+ if (modelId.startsWith(storedId) || storedId.startsWith(modelId)) {
81172
+ return rowToModelPricing(row);
81173
+ }
81174
+ }
81175
+ return null;
81159
81176
  }
81160
81177
  function calculateTokenUnitCost(inputTokens, outputTokens, inputMultiplier, outputMultiplier) {
81161
81178
  if (inputMultiplier === null || outputMultiplier === null) {
@@ -81232,7 +81249,7 @@ function buildSystemPrompt(options2) {
81232
81249
  "You coordinate work across squads, wiki knowledge, installed skills, and direct execution tools.",
81233
81250
  "When a user asks about a project that has a squad, delegate to that squad. When no squad exists, you can do the work directly.",
81234
81251
  "Be practical, concise, and execution-oriented. Use tools when they help you produce a more accurate or durable result.",
81235
- "## Delegation Rules\n- Prefer delegation for project-specific work when a matching squad exists.\n- If no squad exists for the target repository or project, use direct coding and knowledge tools yourself.\n- Use the wiki to remember important information and recover prior context.\n- Use installed skills when they are relevant to the current request.",
81252
+ "## Delegation Rules\n- Prefer delegation for project-specific work when a matching squad exists.\n- If no squad exists for the target repository or project, use direct coding and knowledge tools yourself.\n- When hiring a squad, always pass the user's context about what areas of the codebase or what kind of work the squad will focus on. Extract specific folder paths or project files mentioned by the user and pass them as scanPaths.\n- Use the wiki to remember important information and recover prior context.\n- Use installed skills when they are relevant to the current request.",
81236
81253
  `## Squad Roster
81237
81254
  ${formatSquadRoster(options2.squads)}`,
81238
81255
  formatOptionalSection("## Conversation Summary", options2.conversationSummary ?? ""),
@@ -82232,8 +82249,7 @@ async function persistUsage(member, usageEvents, squadName) {
82232
82249
  for (const usage of usageEvents) {
82233
82250
  const model = usage.model;
82234
82251
  const pricing = await getModelPricing(model);
82235
- const premiumRequestCost = pricing?.premiumMultiplier ?? 0;
82236
- const tokenUnitCost = pricing ? calculateTokenUnitCost(
82252
+ const cost = pricing ? calculateTokenUnitCost(
82237
82253
  usage.inputTokens ?? 0,
82238
82254
  usage.outputTokens ?? 0,
82239
82255
  pricing.tokenInputMultiplier,
@@ -82247,9 +82263,7 @@ async function persistUsage(member, usageEvents, squadName) {
82247
82263
  model,
82248
82264
  inputTokens: usage.inputTokens ?? 0,
82249
82265
  outputTokens: usage.outputTokens ?? 0,
82250
- cost: tokenUnitCost,
82251
- premiumRequestCost,
82252
- tokenUnitCost
82266
+ cost
82253
82267
  });
82254
82268
  }
82255
82269
  }
@@ -82603,9 +82617,10 @@ var ROLE_GENERATION_PROMPT = `You are staffing an autonomous software squad for
82603
82617
  Given repository analysis describing languages, frameworks, file structure, architectural patterns, and operational concerns, propose the smallest effective team that can deliver objectives safely.
82604
82618
 
82605
82619
  Requirements:
82606
- - Mandatory roles Team Lead and QA must always exist.
82620
+ - Mandatory roles Team Lead and QA must always exist \u2014 do NOT include them in your response.
82607
82621
  - Suggest only additional roles that are clearly justified by the repository analysis.
82608
- - Prefer durable role names such as backend-engineer, frontend-engineer, test-automation-engineer, platform-engineer, data-engineer, security-engineer, documentation-engineer, or mobile-engineer.
82622
+ - Role names must reflect Senior or Principal seniority and be specific to the actual technology stack and work scope. Examples: "Principal React Engineer", "Senior DevOps Engineer", "Senior CI/CD Solutions Engineer", "Principal .NET API Engineer", "Senior Data Pipeline Engineer".
82623
+ - Do NOT use generic names like "Frontend Engineer" or "Backend Engineer". Be specific about the technology and domain.
82609
82624
  - Each role must have a short human-readable name and a concise description of responsibilities.
82610
82625
  - Avoid duplicate or overlapping roles.
82611
82626
  - Optimize for implementation, verification, and maintainability.
@@ -82614,9 +82629,9 @@ Return strict JSON in this shape:
82614
82629
  {
82615
82630
  "roles": [
82616
82631
  {
82617
- "role": "frontend-engineer",
82618
- "name": "Frontend Engineer",
82619
- "description": "Owns UI implementation, client state, and browser-facing tests."
82632
+ "role": "principal-react-engineer",
82633
+ "name": "Principal React Engineer",
82634
+ "description": "Owns UI implementation, client state management, and browser-facing integration tests."
82620
82635
  }
82621
82636
  ]
82622
82637
  }`;
@@ -83411,29 +83426,29 @@ function detectRolesFromAnalysis(repoAnalysis) {
83411
83426
  const roles = [];
83412
83427
  if (/(react|next|vue|angular|frontend|ui|xaml)/.test(analysis)) {
83413
83428
  roles.push({
83414
- role: "frontend-engineer",
83415
- name: "Frontend Engineer",
83429
+ role: "senior-frontend-engineer",
83430
+ name: "Senior Frontend Engineer",
83416
83431
  description: "Implements user-facing experiences, UI state, and presentation-layer changes."
83417
83432
  });
83418
83433
  }
83419
83434
  if (/(node|express|api|backend|server|daemon|database|sql|postgres|sqlite)/.test(analysis)) {
83420
83435
  roles.push({
83421
- role: "backend-engineer",
83422
- name: "Backend Engineer",
83436
+ role: "senior-backend-engineer",
83437
+ name: "Senior Backend Engineer",
83423
83438
  description: "Implements server-side logic, data access, integrations, and execution flow changes."
83424
83439
  });
83425
83440
  }
83426
83441
  if (/(infra|docker|kubernetes|deploy|ci|cd|workflow|github actions)/.test(analysis)) {
83427
83442
  roles.push({
83428
- role: "platform-engineer",
83429
- name: "Platform Engineer",
83443
+ role: "senior-platform-engineer",
83444
+ name: "Senior Platform Engineer",
83430
83445
  description: "Owns automation, pipelines, environments, and operational tooling."
83431
83446
  });
83432
83447
  }
83433
83448
  if (/(security|auth|oauth|secret|policy)/.test(analysis)) {
83434
83449
  roles.push({
83435
- role: "security-engineer",
83436
- name: "Security Engineer",
83450
+ role: "senior-security-engineer",
83451
+ name: "Senior Security Engineer",
83437
83452
  description: "Reviews authentication, authorization, secrets handling, and security-sensitive changes."
83438
83453
  });
83439
83454
  }
@@ -83493,13 +83508,17 @@ ${repoContext}`;
83493
83508
  }
83494
83509
  return generateRolePrompt(normalized, repoContext);
83495
83510
  }
83496
- async function proposeSquadComposition(repoUrl, repoAnalysis) {
83511
+ async function proposeSquadComposition(repoUrl, repoAnalysis, context) {
83497
83512
  const mandatory = buildMandatoryRoles();
83498
83513
  const fallbackAdditional = dedupeRoles(detectRolesFromAnalysis(repoAnalysis));
83514
+ const contextSection = context ? `
83515
+
83516
+ Additional context from user:
83517
+ ${context}` : "";
83499
83518
  const prompt = `Repository URL: ${repoUrl}
83500
83519
 
83501
83520
  Repository analysis:
83502
- ${repoAnalysis}
83521
+ ${repoAnalysis}${contextSection}
83503
83522
 
83504
83523
  ${ROLE_GENERATION_PROMPT}`;
83505
83524
  let client2 = null;
@@ -83611,7 +83630,13 @@ async function pathExists2(path) {
83611
83630
  }
83612
83631
  }
83613
83632
  var hireSquadSchema = external_exports.object({
83614
- repoUrl: external_exports.string().trim().min(1)
83633
+ repoUrl: external_exports.string().trim().min(1).describe("The repository URL to hire a squad for"),
83634
+ context: external_exports.string().optional().describe(
83635
+ "Additional context from the user to guide squad composition and role selection. Include any specifics about what areas of the codebase the squad will focus on, what technologies matter most, or what kind of work they will do."
83636
+ ),
83637
+ scanPaths: external_exports.array(external_exports.string()).optional().describe(
83638
+ "Relative paths within the repository to focus the analysis on. When provided, only these directories are scanned for technology detection instead of the entire repo. Extract these from the user's context when they mention specific folders, solution files, or project subsets."
83639
+ )
83615
83640
  });
83616
83641
  var squadIdSchema = external_exports.object({
83617
83642
  squadId: external_exports.string().trim().min(1)
@@ -83620,10 +83645,21 @@ var delegateToSquadSchema = external_exports.object({
83620
83645
  squadId: external_exports.string().trim().min(1),
83621
83646
  objective: external_exports.string().trim().min(1)
83622
83647
  });
83648
+ var renameSquadSchema = external_exports.object({
83649
+ squadId: external_exports.string().trim().min(1),
83650
+ name: external_exports.string().trim().min(1)
83651
+ });
83652
+ var updateSquadMemberSchema = external_exports.object({
83653
+ squadId: external_exports.string().trim().min(1),
83654
+ memberId: external_exports.string().trim().min(1),
83655
+ role: external_exports.string().trim().min(1).optional(),
83656
+ systemPrompt: external_exports.string().optional(),
83657
+ model: external_exports.string().optional()
83658
+ });
83623
83659
  var squadToolDefinitions = [
83624
83660
  {
83625
83661
  name: "hire_squad",
83626
- description: "Hire or refresh a squad for a repository.",
83662
+ description: "Hire or refresh a squad for a repository. Optionally provide context to guide role selection and scanPaths to focus the repo analysis on specific directories.",
83627
83663
  parameters: hireSquadSchema,
83628
83664
  skipPermission: true
83629
83665
  },
@@ -83650,6 +83686,18 @@ var squadToolDefinitions = [
83650
83686
  description: "Create an objective for a squad and start execution.",
83651
83687
  parameters: delegateToSquadSchema,
83652
83688
  skipPermission: true
83689
+ },
83690
+ {
83691
+ name: "rename_squad",
83692
+ description: "Rename a squad.",
83693
+ parameters: renameSquadSchema,
83694
+ skipPermission: true
83695
+ },
83696
+ {
83697
+ name: "update_squad_member",
83698
+ description: "Update a squad member's role, system prompt, and/or default model. All fields are optional; only provided fields are changed.",
83699
+ parameters: updateSquadMemberSchema,
83700
+ skipPermission: true
83653
83701
  }
83654
83702
  ];
83655
83703
  function getDefaultSquadConfig(_config) {
@@ -83659,19 +83707,90 @@ function getDefaultSquadConfig(_config) {
83659
83707
  maxRevisions: QA_MAX_REVISIONS
83660
83708
  };
83661
83709
  }
83662
- async function buildRepoAnalysis(repoUrl) {
83663
- const normalized = repoUrl.trim();
83664
- const segments = normalized.replace(/\.git$/i, "").split("/").filter(Boolean);
83665
- const owner = segments.at(-2) ?? "";
83666
- const name = segments.at(-1) ?? normalized;
83667
- const lines = [`Repository URL: ${normalized}`, `Repository name: ${name}`];
83668
- if (!owner || !name) {
83669
- lines.push(
83670
- "Use the repository identity and any available conventions to propose a practical squad composition."
83671
- );
83672
- return lines.join("\n");
83710
+ var MANIFEST_FILES = [
83711
+ "package.json",
83712
+ "Cargo.toml",
83713
+ "go.mod",
83714
+ "requirements.txt",
83715
+ "pyproject.toml",
83716
+ "Gemfile",
83717
+ "pom.xml",
83718
+ "build.gradle",
83719
+ "composer.json",
83720
+ "Makefile",
83721
+ "Dockerfile",
83722
+ "docker-compose.yml",
83723
+ "docker-compose.yaml",
83724
+ ".github/workflows"
83725
+ ];
83726
+ var SRC_DIR_NAMES = ["src", "lib", "app", "packages", "crates", "cmd", "internal"];
83727
+ var README_CANDIDATES2 = ["README.md", "README.rst", "README.txt", "README"];
83728
+ async function readManifests(rootPath, rootLabel) {
83729
+ const lines = [];
83730
+ for (const manifest of MANIFEST_FILES) {
83731
+ const fullPath = join15(rootPath, manifest);
83732
+ try {
83733
+ const fileStat = await stat4(fullPath);
83734
+ if (fileStat.isFile()) {
83735
+ const content = await readFile9(fullPath, "utf8");
83736
+ lines.push(`
83737
+ --- ${rootLabel}${manifest} ---
83738
+ ${content.slice(0, 3e3)}`);
83739
+ } else if (fileStat.isDirectory()) {
83740
+ const children = await readdir6(fullPath);
83741
+ lines.push(`
83742
+ --- ${rootLabel}${manifest}/ ---
83743
+ ${children.join(", ")}`);
83744
+ }
83745
+ } catch {
83746
+ }
83673
83747
  }
83674
- const repoDir = join15(DATA_DIR, "repos", `${owner}--${name}`);
83748
+ return lines;
83749
+ }
83750
+ async function scanSrcDirs(rootPath, rootLabel, dirs) {
83751
+ const lines = [];
83752
+ const srcDirs = dirs.filter((d) => SRC_DIR_NAMES.includes(d));
83753
+ for (const srcDir of srcDirs) {
83754
+ try {
83755
+ const srcEntries = await readdir6(join15(rootPath, srcDir), { withFileTypes: true });
83756
+ const srcFiles = srcEntries.filter((e) => e.isFile()).map((e) => e.name);
83757
+ const srcSubDirs = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name);
83758
+ lines.push(`
83759
+ --- ${rootLabel}${srcDir}/ ---`);
83760
+ if (srcSubDirs.length) lines.push(` Directories: ${srcSubDirs.join(", ")}`);
83761
+ if (srcFiles.length) lines.push(` Files: ${srcFiles.slice(0, 30).join(", ")}`);
83762
+ } catch {
83763
+ }
83764
+ }
83765
+ return lines;
83766
+ }
83767
+ async function findReadme(rootPath, rootLabel) {
83768
+ for (const readme of README_CANDIDATES2) {
83769
+ try {
83770
+ const content = await readFile9(join15(rootPath, readme), "utf8");
83771
+ return `
83772
+ --- ${rootLabel}${readme} (excerpt) ---
83773
+ ${content.slice(0, 1500)}`;
83774
+ } catch {
83775
+ }
83776
+ }
83777
+ return null;
83778
+ }
83779
+ async function scanRoot(root) {
83780
+ const lines = [];
83781
+ const rootLabel = root.label ? `${root.label}/` : "";
83782
+ const rootEntries = await readdir6(root.path, { withFileTypes: true });
83783
+ const rootFiles = rootEntries.filter((e) => e.isFile()).map((e) => e.name);
83784
+ const rootDirs = rootEntries.filter((e) => e.isDirectory() && e.name !== ".git").map((e) => e.name);
83785
+ if (rootFiles.length) lines.push(`${rootLabel}Files: ${rootFiles.join(", ")}`);
83786
+ if (rootDirs.length) lines.push(`${rootLabel}Directories: ${rootDirs.join(", ")}`);
83787
+ lines.push(...await readManifests(root.path, rootLabel));
83788
+ lines.push(...await scanSrcDirs(root.path, rootLabel, rootDirs));
83789
+ const readme = await findReadme(root.path, rootLabel);
83790
+ if (readme) lines.push(readme);
83791
+ return lines;
83792
+ }
83793
+ async function cloneOrUpdateRepo(normalized, repoDir) {
83675
83794
  try {
83676
83795
  await mkdir10(join15(DATA_DIR, "repos"), { recursive: true });
83677
83796
  if (await pathExists2(join15(repoDir, ".git"))) {
@@ -83683,79 +83802,40 @@ async function buildRepoAnalysis(repoUrl) {
83683
83802
  timeout: 6e4
83684
83803
  });
83685
83804
  }
83805
+ return true;
83686
83806
  } catch {
83807
+ return false;
83808
+ }
83809
+ }
83810
+ async function buildRepoAnalysis(repoUrl, scanPaths) {
83811
+ const normalized = repoUrl.trim();
83812
+ const segments = normalized.replace(/\.git$/i, "").split("/").filter(Boolean);
83813
+ const owner = segments.at(-2) ?? "";
83814
+ const name = segments.at(-1) ?? normalized;
83815
+ const lines = [`Repository URL: ${normalized}`, `Repository name: ${name}`];
83816
+ if (!owner || !name) {
83817
+ lines.push(
83818
+ "Use the repository identity and any available conventions to propose a practical squad composition."
83819
+ );
83820
+ return lines.join("\n");
83821
+ }
83822
+ const repoDir = join15(DATA_DIR, "repos", `${owner}--${name}`);
83823
+ const cloned = await cloneOrUpdateRepo(normalized, repoDir);
83824
+ if (!cloned) {
83687
83825
  lines.push("Unable to clone repository locally; falling back to basic analysis.");
83688
83826
  lines.push("Based on the repository name, propose roles that match common project patterns.");
83689
83827
  return lines.join("\n");
83690
83828
  }
83691
- try {
83692
- const rootEntries = await readdir6(repoDir, { withFileTypes: true });
83693
- const rootFiles = rootEntries.filter((e) => e.isFile()).map((e) => e.name);
83694
- const rootDirs = rootEntries.filter((e) => e.isDirectory() && e.name !== ".git").map((e) => e.name);
83695
- if (rootFiles.length) lines.push(`Root files: ${rootFiles.join(", ")}`);
83696
- if (rootDirs.length) lines.push(`Root directories: ${rootDirs.join(", ")}`);
83697
- const manifestFiles = [
83698
- "package.json",
83699
- "Cargo.toml",
83700
- "go.mod",
83701
- "requirements.txt",
83702
- "pyproject.toml",
83703
- "Gemfile",
83704
- "pom.xml",
83705
- "build.gradle",
83706
- "composer.json",
83707
- "Makefile",
83708
- "Dockerfile",
83709
- "docker-compose.yml",
83710
- "docker-compose.yaml",
83711
- ".github/workflows"
83712
- ];
83713
- for (const manifest of manifestFiles) {
83714
- const fullPath = join15(repoDir, manifest);
83715
- try {
83716
- const fileStat = await stat4(fullPath);
83717
- if (fileStat.isFile()) {
83718
- const content = await readFile9(fullPath, "utf8");
83719
- lines.push(`
83720
- --- ${manifest} ---
83721
- ${content.slice(0, 3e3)}`);
83722
- } else if (fileStat.isDirectory()) {
83723
- const children = await readdir6(fullPath);
83724
- lines.push(`
83725
- --- ${manifest}/ ---
83726
- ${children.join(", ")}`);
83727
- }
83728
- } catch {
83729
- }
83730
- }
83731
- const srcDirs = rootDirs.filter(
83732
- (d) => ["src", "lib", "app", "packages", "crates", "cmd", "internal"].includes(d)
83733
- );
83734
- for (const srcDir of srcDirs) {
83735
- try {
83736
- const srcEntries = await readdir6(join15(repoDir, srcDir), { withFileTypes: true });
83737
- const srcFiles = srcEntries.filter((e) => e.isFile()).map((e) => e.name);
83738
- const srcSubDirs = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name);
83739
- lines.push(`
83740
- --- ${srcDir}/ ---`);
83741
- if (srcSubDirs.length) lines.push(` Directories: ${srcSubDirs.join(", ")}`);
83742
- if (srcFiles.length) lines.push(` Files: ${srcFiles.slice(0, 30).join(", ")}`);
83743
- } catch {
83744
- }
83745
- }
83746
- const readmeCandidates = ["README.md", "README.rst", "README.txt", "README"];
83747
- for (const readme of readmeCandidates) {
83748
- try {
83749
- const content = await readFile9(join15(repoDir, readme), "utf8");
83750
- lines.push(`
83751
- --- ${readme} (excerpt) ---
83752
- ${content.slice(0, 1500)}`);
83753
- break;
83754
- } catch {
83755
- }
83829
+ const scanRoots = scanPaths && scanPaths.length > 0 ? scanPaths.map((p) => ({ path: join15(repoDir, p), label: p })) : [{ path: repoDir, label: "" }];
83830
+ if (scanPaths && scanPaths.length > 0) {
83831
+ lines.push(`Focused scan paths: ${scanPaths.join(", ")}`);
83832
+ }
83833
+ for (const root of scanRoots) {
83834
+ try {
83835
+ lines.push(...await scanRoot(root));
83836
+ } catch {
83837
+ lines.push(`Filesystem scan failed for ${root.label || "repo root"}; skipping.`);
83756
83838
  }
83757
- } catch {
83758
- lines.push("Filesystem scan failed; using minimal info.");
83759
83839
  }
83760
83840
  lines.push(
83761
83841
  "\nBased on the above repository structure and contents, propose roles that match the project's actual technology stack and architecture."
@@ -83770,6 +83850,22 @@ function formatSquadList(squads) {
83770
83850
  (squad) => `${squad.id}: ${squad.name} (${squad.repoOwner}/${squad.repoName}) [${squad.status}]`
83771
83851
  ).join("\n");
83772
83852
  }
83853
+ async function processQueue2(squadId) {
83854
+ try {
83855
+ const running = await countRunningInstances(squadId);
83856
+ const config2 = (await Promise.resolve().then(() => (init_config(), config_exports))).loadConfig();
83857
+ if (running < config2.maxInstancesPerSquad) {
83858
+ const next = await getNextQueued(squadId);
83859
+ if (next) {
83860
+ const freshSquad = await getSquad(squadId);
83861
+ if (freshSquad) {
83862
+ void startAndExecuteInstance(next.id, freshSquad, next.objectiveId);
83863
+ }
83864
+ }
83865
+ }
83866
+ } catch {
83867
+ }
83868
+ }
83773
83869
  async function startAndExecuteInstance(instanceId, squad, objectiveId) {
83774
83870
  try {
83775
83871
  const repoPath = await resolveRepoPath(squad.repoUrl, squad.repoName);
@@ -83790,56 +83886,96 @@ async function startAndExecuteInstance(instanceId, squad, objectiveId) {
83790
83886
  await failInstance(instanceId, message2).catch(() => {
83791
83887
  });
83792
83888
  } finally {
83793
- try {
83794
- const running = await countRunningInstances(squad.id);
83795
- const config2 = (await Promise.resolve().then(() => (init_config(), config_exports))).loadConfig();
83796
- if (running < config2.maxInstancesPerSquad) {
83797
- const next = await getNextQueued(squad.id);
83798
- if (next) {
83799
- const freshSquad = await getSquad(squad.id);
83800
- if (freshSquad) {
83801
- void startAndExecuteInstance(next.id, freshSquad, next.objectiveId);
83802
- }
83803
- }
83804
- }
83805
- } catch {
83806
- }
83889
+ await processQueue2(squad.id);
83890
+ }
83891
+ }
83892
+ async function handleHireSquad(rawArgs, config2) {
83893
+ const { repoUrl, context, scanPaths } = hireSquadSchema.parse(rawArgs);
83894
+ const composition = await proposeSquadComposition(
83895
+ repoUrl,
83896
+ await buildRepoAnalysis(repoUrl, scanPaths),
83897
+ context
83898
+ );
83899
+ const result = await hireSquad(repoUrl, composition, getDefaultSquadConfig(config2));
83900
+ return {
83901
+ message: `Squad ready for ${repoUrl}.`,
83902
+ squad: result.squad,
83903
+ members: result.members
83904
+ };
83905
+ }
83906
+ async function handleFireSquad(rawArgs) {
83907
+ const { squadId } = squadIdSchema.parse(rawArgs);
83908
+ const squad = await getSquad(squadId);
83909
+ if (!squad) {
83910
+ throw new Error(`Squad ${squadId} was not found.`);
83911
+ }
83912
+ const activeObjectives = await getActiveObjectives(squadId);
83913
+ if (activeObjectives.length > 0) {
83914
+ const updated = await updateSquad(squadId, { status: "inactive" });
83915
+ return {
83916
+ message: `Squad ${squadId} was deactivated because it still has active objectives.`,
83917
+ squad: updated,
83918
+ activeObjectives
83919
+ };
83920
+ }
83921
+ await deleteSquad(squadId);
83922
+ return { message: `Squad ${squadId} was deleted.`, squadId };
83923
+ }
83924
+ async function handleDelegateToSquad(rawArgs) {
83925
+ const { squadId, objective } = delegateToSquadSchema.parse(rawArgs);
83926
+ const squad = await getSquad(squadId);
83927
+ if (!squad) {
83928
+ throw new Error(`Squad ${squadId} was not found.`);
83929
+ }
83930
+ const createdObjective = await createObjective(squadId, objective);
83931
+ const { instance, queued } = await spawnInstance({
83932
+ squadId,
83933
+ objectiveId: createdObjective.id
83934
+ });
83935
+ if (!queued) {
83936
+ void startAndExecuteInstance(instance.id, squad, createdObjective.id);
83937
+ }
83938
+ return {
83939
+ message: queued ? `Objective queued for squad ${squad.name} (at capacity). Instance ${instance.id} will start when a slot opens.` : `Delegated objective to squad ${squad.name}. Instance ${instance.id} started.`,
83940
+ objective: createdObjective,
83941
+ instanceId: instance.id,
83942
+ queued
83943
+ };
83944
+ }
83945
+ async function handleUpdateSquadMember(rawArgs) {
83946
+ const { squadId, memberId, role, systemPrompt, model } = updateSquadMemberSchema.parse(rawArgs);
83947
+ const squad = await getSquad(squadId);
83948
+ if (!squad) {
83949
+ throw new Error(`Squad ${squadId} was not found.`);
83950
+ }
83951
+ const member = await getMember(memberId);
83952
+ if (!member || member.squadId !== squadId) {
83953
+ throw new Error(`Member ${memberId} was not found in squad ${squadId}.`);
83807
83954
  }
83955
+ const updated = await updateMember(memberId, {
83956
+ role,
83957
+ systemPrompt,
83958
+ model: model === "" ? null : model
83959
+ });
83960
+ if (!updated) {
83961
+ throw new Error(`Failed to update member ${memberId}.`);
83962
+ }
83963
+ eventBus.emit(EVENT_NAMES.SQUAD_MEMBER_UPDATED, {
83964
+ squadId,
83965
+ member: updated
83966
+ });
83967
+ return {
83968
+ message: `Updated member "${updated.name}" in squad "${squad.name}".`,
83969
+ member: updated
83970
+ };
83808
83971
  }
83809
83972
  function createSquadToolExecutor(config2) {
83810
83973
  return async (toolName, rawArgs) => {
83811
83974
  switch (toolName) {
83812
- case "hire_squad": {
83813
- const { repoUrl } = hireSquadSchema.parse(rawArgs);
83814
- const composition = await proposeSquadComposition(
83815
- repoUrl,
83816
- await buildRepoAnalysis(repoUrl)
83817
- );
83818
- const result = await hireSquad(repoUrl, composition, getDefaultSquadConfig(config2));
83819
- return {
83820
- message: `Squad ready for ${repoUrl}.`,
83821
- squad: result.squad,
83822
- members: result.members
83823
- };
83824
- }
83825
- case "fire_squad": {
83826
- const { squadId } = squadIdSchema.parse(rawArgs);
83827
- const squad = await getSquad(squadId);
83828
- if (!squad) {
83829
- throw new Error(`Squad ${squadId} was not found.`);
83830
- }
83831
- const activeObjectives = await getActiveObjectives(squadId);
83832
- if (activeObjectives.length > 0) {
83833
- const updated = await updateSquad(squadId, { status: "inactive" });
83834
- return {
83835
- message: `Squad ${squadId} was deactivated because it still has active objectives.`,
83836
- squad: updated,
83837
- activeObjectives
83838
- };
83839
- }
83840
- await deleteSquad(squadId);
83841
- return { message: `Squad ${squadId} was deleted.`, squadId };
83842
- }
83975
+ case "hire_squad":
83976
+ return handleHireSquad(rawArgs, config2);
83977
+ case "fire_squad":
83978
+ return handleFireSquad(rawArgs);
83843
83979
  case "list_squads": {
83844
83980
  const squads = await listSquads();
83845
83981
  return {
@@ -83855,27 +83991,22 @@ function createSquadToolExecutor(config2) {
83855
83991
  ...status
83856
83992
  };
83857
83993
  }
83858
- case "delegate_to_squad": {
83859
- const { squadId, objective } = delegateToSquadSchema.parse(rawArgs);
83860
- const squad = await getSquad(squadId);
83861
- if (!squad) {
83994
+ case "delegate_to_squad":
83995
+ return handleDelegateToSquad(rawArgs);
83996
+ case "rename_squad": {
83997
+ const { squadId, name } = renameSquadSchema.parse(rawArgs);
83998
+ const updated = await updateSquad(squadId, { name });
83999
+ if (!updated) {
83862
84000
  throw new Error(`Squad ${squadId} was not found.`);
83863
84001
  }
83864
- const createdObjective = await createObjective(squadId, objective);
83865
- const { instance, queued } = await spawnInstance({
83866
- squadId,
83867
- objectiveId: createdObjective.id
83868
- });
83869
- if (!queued) {
83870
- void startAndExecuteInstance(instance.id, squad, createdObjective.id);
83871
- }
84002
+ eventBus.emit(EVENT_NAMES.SQUAD_UPDATED, { squad: updated });
83872
84003
  return {
83873
- message: queued ? `Objective queued for squad ${squad.name} (at capacity). Instance ${instance.id} will start when a slot opens.` : `Delegated objective to squad ${squad.name}. Instance ${instance.id} started.`,
83874
- objective: createdObjective,
83875
- instanceId: instance.id,
83876
- queued
84004
+ message: `Squad renamed to "${updated.name}".`,
84005
+ squad: updated
83877
84006
  };
83878
84007
  }
84008
+ case "update_squad_member":
84009
+ return handleUpdateSquadMember(rawArgs);
83879
84010
  default:
83880
84011
  throw new Error(`Unsupported squad tool: ${toolName}`);
83881
84012
  }
@@ -83942,6 +84073,30 @@ function buildMemoryPath(timestamp) {
83942
84073
  const time3 = iso.slice(11, 19).replace(/:/gu, "-");
83943
84074
  return `memory/${date5}/${time3}.md`;
83944
84075
  }
84076
+ async function handleWikiWrite(rawArgs) {
84077
+ const { path, title, content, tags } = wikiWriteSchema.parse(rawArgs);
84078
+ const existing = await getPage(path);
84079
+ const page = existing ? await updatePage(path, { title, content, tags: tags ?? [] }) : await createPage(path, title, content, tags ?? []);
84080
+ return {
84081
+ message: `${existing ? "Updated" : "Created"} wiki page ${path}.`,
84082
+ page
84083
+ };
84084
+ }
84085
+ async function handleRecall(rawArgs) {
84086
+ const { query } = recallSchema.parse(rawArgs);
84087
+ const [matches, recents] = await Promise.all([searchPages(query, 1), getRecentPages(1)]);
84088
+ const page = matches[0] ?? recents[0] ?? null;
84089
+ if (!page) {
84090
+ return { message: "No wiki content is available yet." };
84091
+ }
84092
+ return {
84093
+ message: `Best match: ${page.title}`,
84094
+ path: page.path,
84095
+ title: page.title,
84096
+ content: page.content,
84097
+ tags: page.tags
84098
+ };
84099
+ }
83945
84100
  var executeWikiToolCall = async (toolName, rawArgs) => {
83946
84101
  switch (toolName) {
83947
84102
  case "wiki_read": {
@@ -83952,15 +84107,8 @@ var executeWikiToolCall = async (toolName, rawArgs) => {
83952
84107
  }
83953
84108
  return { page };
83954
84109
  }
83955
- case "wiki_write": {
83956
- const { path, title, content, tags } = wikiWriteSchema.parse(rawArgs);
83957
- const existing = await getPage(path);
83958
- const page = existing ? await updatePage(path, { title, content, tags: tags ?? [] }) : await createPage(path, title, content, tags ?? []);
83959
- return {
83960
- message: `${existing ? "Updated" : "Created"} wiki page ${path}.`,
83961
- page
83962
- };
83963
- }
84110
+ case "wiki_write":
84111
+ return handleWikiWrite(rawArgs);
83964
84112
  case "wiki_search": {
83965
84113
  const { query, limit } = wikiSearchSchema.parse(rawArgs);
83966
84114
  const pages = await searchPages(query, limit ?? 5);
@@ -83980,21 +84128,8 @@ var executeWikiToolCall = async (toolName, rawArgs) => {
83980
84128
  page
83981
84129
  };
83982
84130
  }
83983
- case "recall": {
83984
- const { query } = recallSchema.parse(rawArgs);
83985
- const [matches, recents] = await Promise.all([searchPages(query, 1), getRecentPages(1)]);
83986
- const page = matches[0] ?? recents[0] ?? null;
83987
- if (!page) {
83988
- return { message: "No wiki content is available yet." };
83989
- }
83990
- return {
83991
- message: `Best match: ${page.title}`,
83992
- path: page.path,
83993
- title: page.title,
83994
- content: page.content,
83995
- tags: page.tags
83996
- };
83997
- }
84131
+ case "recall":
84132
+ return handleRecall(rawArgs);
83998
84133
  default:
83999
84134
  throw new Error(`Unsupported wiki tool: ${toolName}`);
84000
84135
  }
@@ -84273,8 +84408,7 @@ var Orchestrator = class {
84273
84408
  }
84274
84409
  const model = usage.model || this.activeModel || this.config.defaultModel;
84275
84410
  const pricing = await getModelPricing(model);
84276
- const premiumRequestCost = pricing?.premiumMultiplier ?? 0;
84277
- const tokenUnitCost = pricing ? calculateTokenUnitCost(
84411
+ const cost = pricing ? calculateTokenUnitCost(
84278
84412
  usage.inputTokens,
84279
84413
  usage.outputTokens,
84280
84414
  pricing.tokenInputMultiplier,
@@ -84284,9 +84418,7 @@ var Orchestrator = class {
84284
84418
  model,
84285
84419
  inputTokens: usage.inputTokens,
84286
84420
  outputTokens: usage.outputTokens,
84287
- cost: tokenUnitCost,
84288
- premiumRequestCost,
84289
- tokenUnitCost
84421
+ cost
84290
84422
  });
84291
84423
  }
84292
84424
  };