heyio 4.2.3 → 4.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/daemon/cli.js +437 -366
- package/dist/daemon/index.js +437 -357
- package/package.json +1 -1
package/dist/daemon/cli.js
CHANGED
|
@@ -75,19 +75,18 @@ var init_api = __esm({
|
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
// packages/shared/dist/constants.js
|
|
78
|
-
var APP_NAME, APP_VERSION, API_PORT, API_HOST, DEFAULT_MODEL, SESSION_RESET_THRESHOLD, SCHEDULER_INTERVAL_MS, QA_MAX_REVISIONS,
|
|
78
|
+
var APP_NAME, APP_VERSION, API_PORT, API_HOST, DEFAULT_MODEL, SESSION_RESET_THRESHOLD, SCHEDULER_INTERVAL_MS, QA_MAX_REVISIONS, EVENT_NAMES;
|
|
79
79
|
var init_constants = __esm({
|
|
80
80
|
"packages/shared/dist/constants.js"() {
|
|
81
81
|
"use strict";
|
|
82
82
|
APP_NAME = "io";
|
|
83
|
-
APP_VERSION = "4.2.
|
|
83
|
+
APP_VERSION = "4.2.5";
|
|
84
84
|
API_PORT = 7777;
|
|
85
85
|
API_HOST = "0.0.0.0";
|
|
86
86
|
DEFAULT_MODEL = "gpt-4o";
|
|
87
87
|
SESSION_RESET_THRESHOLD = 50;
|
|
88
88
|
SCHEDULER_INTERVAL_MS = 6e4;
|
|
89
89
|
QA_MAX_REVISIONS = 3;
|
|
90
|
-
MANDATORY_ROLES = ["team-lead", "qa"];
|
|
91
90
|
EVENT_NAMES = {
|
|
92
91
|
SQUAD_CREATED: "squad.created",
|
|
93
92
|
SQUAD_DELETED: "squad.deleted",
|
|
@@ -67986,6 +67985,10 @@ async function scrapeTokenUnitPricing() {
|
|
|
67986
67985
|
const html = await fetchPage(TOKEN_UNIT_COSTS_URL);
|
|
67987
67986
|
return parseTokenUnitTable(html);
|
|
67988
67987
|
}
|
|
67988
|
+
async function scrapeCopilotPricing() {
|
|
67989
|
+
const markdown = await fetchMarkdown(COPILOT_PRICING_URL);
|
|
67990
|
+
return parseCopilotPricingMarkdown(markdown);
|
|
67991
|
+
}
|
|
67989
67992
|
async function scrapePremiumRequestPricing() {
|
|
67990
67993
|
const html = await fetchPage(PREMIUM_MULTIPLIERS_URL);
|
|
67991
67994
|
return parsePremiumMultiplierTable(html);
|
|
@@ -68003,6 +68006,19 @@ async function fetchPage(url2) {
|
|
|
68003
68006
|
}
|
|
68004
68007
|
return response.text();
|
|
68005
68008
|
}
|
|
68009
|
+
async function fetchMarkdown(url2) {
|
|
68010
|
+
const response = await fetch(url2, {
|
|
68011
|
+
headers: {
|
|
68012
|
+
Accept: "text/markdown, text/plain, */*",
|
|
68013
|
+
"User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
|
|
68014
|
+
},
|
|
68015
|
+
redirect: "follow"
|
|
68016
|
+
});
|
|
68017
|
+
if (!response.ok) {
|
|
68018
|
+
throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
|
|
68019
|
+
}
|
|
68020
|
+
return response.text();
|
|
68021
|
+
}
|
|
68006
68022
|
function parseTokenUnitTable(html) {
|
|
68007
68023
|
const results = [];
|
|
68008
68024
|
const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]*)<\/td>\s*<td[^>]*>([^<]+)<\/td>/gi;
|
|
@@ -68037,12 +68053,82 @@ function parsePremiumMultiplierTable(html) {
|
|
|
68037
68053
|
function cleanCellText(text) {
|
|
68038
68054
|
return text.replace(/<[^>]*>/g, "").replace(/&[^;]+;/g, " ").trim();
|
|
68039
68055
|
}
|
|
68040
|
-
|
|
68056
|
+
function parseCopilotPricingMarkdown(markdown) {
|
|
68057
|
+
const results = [];
|
|
68058
|
+
const seenModels = /* @__PURE__ */ new Set();
|
|
68059
|
+
const lines = markdown.split("\n");
|
|
68060
|
+
let columnIndices = null;
|
|
68061
|
+
for (const line of lines) {
|
|
68062
|
+
const trimmed = line.trim();
|
|
68063
|
+
if (!trimmed.startsWith("|")) continue;
|
|
68064
|
+
if (/\|\s*Model\s/i.test(trimmed)) {
|
|
68065
|
+
columnIndices = parseHeaderColumns(trimmed);
|
|
68066
|
+
continue;
|
|
68067
|
+
}
|
|
68068
|
+
if (/^[\s|:-]+$/.test(trimmed) || !columnIndices) continue;
|
|
68069
|
+
const entry = parseDataRow(trimmed, columnIndices);
|
|
68070
|
+
if (entry && !seenModels.has(entry.modelName.toLowerCase())) {
|
|
68071
|
+
seenModels.add(entry.modelName.toLowerCase());
|
|
68072
|
+
results.push(entry);
|
|
68073
|
+
}
|
|
68074
|
+
}
|
|
68075
|
+
return results;
|
|
68076
|
+
}
|
|
68077
|
+
function parseDataRow(line, columnIndices) {
|
|
68078
|
+
const cells = splitMarkdownRow(line);
|
|
68079
|
+
if (cells.length <= columnIndices.output) return null;
|
|
68080
|
+
const modelName = cells[columnIndices.model].trim();
|
|
68081
|
+
const inputPrice = parseDollarValue(cells[columnIndices.input]);
|
|
68082
|
+
const cachedPrice = columnIndices.cached >= 0 ? parseDollarValue(cells[columnIndices.cached]) : null;
|
|
68083
|
+
const outputPrice = parseDollarValue(cells[columnIndices.output]);
|
|
68084
|
+
if (!modelName || inputPrice === null || outputPrice === null) return null;
|
|
68085
|
+
return {
|
|
68086
|
+
modelName,
|
|
68087
|
+
inputMultiplier: inputPrice * PRICE_TO_MULTIPLIER,
|
|
68088
|
+
cachedInputMultiplier: cachedPrice !== null ? cachedPrice * PRICE_TO_MULTIPLIER : null,
|
|
68089
|
+
outputMultiplier: outputPrice * PRICE_TO_MULTIPLIER
|
|
68090
|
+
};
|
|
68091
|
+
}
|
|
68092
|
+
function parseHeaderColumns(headerLine) {
|
|
68093
|
+
const cells = splitMarkdownRow(headerLine).map((c) => c.trim().toLowerCase());
|
|
68094
|
+
const model = cells.findIndex((c) => c === "model" || c === "model name");
|
|
68095
|
+
const input2 = cells.findIndex(
|
|
68096
|
+
(c) => (c === "input" || c === "input multiplier") && !c.includes("cached")
|
|
68097
|
+
);
|
|
68098
|
+
const cached2 = cells.findIndex(
|
|
68099
|
+
(c) => c.includes("cached input") || c === "cached input multiplier"
|
|
68100
|
+
);
|
|
68101
|
+
const output2 = cells.findIndex(
|
|
68102
|
+
(c) => (c === "output" || c === "output multiplier") && !c.includes("cached")
|
|
68103
|
+
);
|
|
68104
|
+
if (model < 0 || input2 < 0 || output2 < 0) {
|
|
68105
|
+
return null;
|
|
68106
|
+
}
|
|
68107
|
+
return { model, input: input2, cached: cached2 >= 0 ? cached2 : -1, output: output2 };
|
|
68108
|
+
}
|
|
68109
|
+
function splitMarkdownRow(line) {
|
|
68110
|
+
const parts = line.split("|");
|
|
68111
|
+
if (parts[0].trim() === "") parts.shift();
|
|
68112
|
+
if (parts[parts.length - 1]?.trim() === "") parts.pop();
|
|
68113
|
+
return parts;
|
|
68114
|
+
}
|
|
68115
|
+
function parseDollarValue(cell) {
|
|
68116
|
+
if (!cell) return null;
|
|
68117
|
+
const cleaned = cell.trim().replace(/[$,]/g, "");
|
|
68118
|
+
if (cleaned.toLowerCase() === "n/a" || cleaned === "" || cleaned === "-") {
|
|
68119
|
+
return null;
|
|
68120
|
+
}
|
|
68121
|
+
const value = Number.parseFloat(cleaned);
|
|
68122
|
+
return Number.isNaN(value) ? null : value;
|
|
68123
|
+
}
|
|
68124
|
+
var TOKEN_UNIT_COSTS_URL, PREMIUM_MULTIPLIERS_URL, COPILOT_PRICING_URL, PRICE_TO_MULTIPLIER;
|
|
68041
68125
|
var init_pricing_scraper = __esm({
|
|
68042
68126
|
"packages/daemon/src/models/pricing-scraper.ts"() {
|
|
68043
68127
|
"use strict";
|
|
68044
68128
|
TOKEN_UNIT_COSTS_URL = "https://docs.github.com/en/billing/reference/costs-for-github-models";
|
|
68045
68129
|
PREMIUM_MULTIPLIERS_URL = "https://docs.github.com/en/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans";
|
|
68130
|
+
COPILOT_PRICING_URL = "https://docs.github.com/api/article/body?pathname=/en/copilot/reference/copilot-billing/models-and-pricing";
|
|
68131
|
+
PRICE_TO_MULTIPLIER = 0.1;
|
|
68046
68132
|
}
|
|
68047
68133
|
});
|
|
68048
68134
|
|
|
@@ -68076,79 +68162,99 @@ var init_seed = __esm({
|
|
|
68076
68162
|
id: "gpt-5-mini",
|
|
68077
68163
|
displayName: "GPT-5 mini",
|
|
68078
68164
|
premiumMultiplier: 0.33,
|
|
68079
|
-
tokenInputMultiplier:
|
|
68080
|
-
tokenOutputMultiplier:
|
|
68081
|
-
cachedInputMultiplier:
|
|
68165
|
+
tokenInputMultiplier: 0.025,
|
|
68166
|
+
tokenOutputMultiplier: 0.2,
|
|
68167
|
+
cachedInputMultiplier: 25e-4,
|
|
68082
68168
|
tier: "trivial",
|
|
68083
68169
|
available: true
|
|
68084
68170
|
},
|
|
68085
68171
|
{
|
|
68086
|
-
id: "
|
|
68087
|
-
displayName: "
|
|
68172
|
+
id: "gpt-5.3-codex",
|
|
68173
|
+
displayName: "GPT-5.3-Codex",
|
|
68088
68174
|
premiumMultiplier: 6,
|
|
68089
|
-
tokenInputMultiplier: 0.
|
|
68090
|
-
tokenOutputMultiplier:
|
|
68091
|
-
cachedInputMultiplier:
|
|
68175
|
+
tokenInputMultiplier: 0.175,
|
|
68176
|
+
tokenOutputMultiplier: 1.4,
|
|
68177
|
+
cachedInputMultiplier: 0.0175,
|
|
68092
68178
|
tier: "premium",
|
|
68093
68179
|
available: true
|
|
68094
68180
|
},
|
|
68095
68181
|
{
|
|
68096
|
-
id: "
|
|
68097
|
-
displayName: "
|
|
68098
|
-
premiumMultiplier:
|
|
68099
|
-
tokenInputMultiplier:
|
|
68100
|
-
tokenOutputMultiplier:
|
|
68101
|
-
cachedInputMultiplier:
|
|
68102
|
-
tier: "
|
|
68182
|
+
id: "gpt-5.4",
|
|
68183
|
+
displayName: "GPT-5.4",
|
|
68184
|
+
premiumMultiplier: 6,
|
|
68185
|
+
tokenInputMultiplier: 0.25,
|
|
68186
|
+
tokenOutputMultiplier: 1.5,
|
|
68187
|
+
cachedInputMultiplier: 0.025,
|
|
68188
|
+
tier: "premium",
|
|
68103
68189
|
available: true
|
|
68104
68190
|
},
|
|
68105
68191
|
{
|
|
68106
|
-
id: "
|
|
68107
|
-
displayName: "
|
|
68108
|
-
premiumMultiplier:
|
|
68109
|
-
tokenInputMultiplier:
|
|
68110
|
-
tokenOutputMultiplier:
|
|
68111
|
-
cachedInputMultiplier:
|
|
68112
|
-
tier: "
|
|
68192
|
+
id: "gpt-5.4-mini",
|
|
68193
|
+
displayName: "GPT-5.4 mini",
|
|
68194
|
+
premiumMultiplier: 6,
|
|
68195
|
+
tokenInputMultiplier: 0.075,
|
|
68196
|
+
tokenOutputMultiplier: 0.45,
|
|
68197
|
+
cachedInputMultiplier: 75e-4,
|
|
68198
|
+
tier: "premium",
|
|
68113
68199
|
available: true
|
|
68114
68200
|
},
|
|
68115
68201
|
{
|
|
68116
|
-
id: "gpt-5.
|
|
68117
|
-
displayName: "GPT-5.
|
|
68118
|
-
premiumMultiplier:
|
|
68119
|
-
tokenInputMultiplier:
|
|
68120
|
-
tokenOutputMultiplier:
|
|
68121
|
-
cachedInputMultiplier:
|
|
68122
|
-
tier: "
|
|
68202
|
+
id: "gpt-5.5",
|
|
68203
|
+
displayName: "GPT-5.5",
|
|
68204
|
+
premiumMultiplier: 57,
|
|
68205
|
+
tokenInputMultiplier: 0.5,
|
|
68206
|
+
tokenOutputMultiplier: 3,
|
|
68207
|
+
cachedInputMultiplier: 0.05,
|
|
68208
|
+
tier: "ultra",
|
|
68123
68209
|
available: true
|
|
68124
68210
|
},
|
|
68125
68211
|
{
|
|
68126
|
-
id: "
|
|
68127
|
-
displayName: "
|
|
68128
|
-
premiumMultiplier:
|
|
68129
|
-
tokenInputMultiplier:
|
|
68130
|
-
tokenOutputMultiplier:
|
|
68131
|
-
cachedInputMultiplier:
|
|
68132
|
-
tier: "
|
|
68212
|
+
id: "claude-sonnet-4",
|
|
68213
|
+
displayName: "Claude Sonnet 4",
|
|
68214
|
+
premiumMultiplier: 6,
|
|
68215
|
+
tokenInputMultiplier: 0.3,
|
|
68216
|
+
tokenOutputMultiplier: 1.5,
|
|
68217
|
+
cachedInputMultiplier: 0.03,
|
|
68218
|
+
tier: "premium",
|
|
68133
68219
|
available: true
|
|
68134
68220
|
},
|
|
68135
68221
|
{
|
|
68136
|
-
id: "
|
|
68137
|
-
displayName: "
|
|
68222
|
+
id: "claude-sonnet-4.5",
|
|
68223
|
+
displayName: "Claude Sonnet 4.5",
|
|
68138
68224
|
premiumMultiplier: 6,
|
|
68139
|
-
tokenInputMultiplier:
|
|
68140
|
-
tokenOutputMultiplier:
|
|
68141
|
-
cachedInputMultiplier:
|
|
68225
|
+
tokenInputMultiplier: 0.3,
|
|
68226
|
+
tokenOutputMultiplier: 1.5,
|
|
68227
|
+
cachedInputMultiplier: 0.03,
|
|
68142
68228
|
tier: "premium",
|
|
68143
68229
|
available: true
|
|
68144
68230
|
},
|
|
68231
|
+
{
|
|
68232
|
+
id: "claude-sonnet-4.6",
|
|
68233
|
+
displayName: "Claude Sonnet 4.6",
|
|
68234
|
+
premiumMultiplier: 9,
|
|
68235
|
+
tokenInputMultiplier: 0.3,
|
|
68236
|
+
tokenOutputMultiplier: 1.5,
|
|
68237
|
+
cachedInputMultiplier: 0.03,
|
|
68238
|
+
tier: "premium",
|
|
68239
|
+
available: true
|
|
68240
|
+
},
|
|
68241
|
+
{
|
|
68242
|
+
id: "claude-haiku-4.5",
|
|
68243
|
+
displayName: "Claude Haiku 4.5",
|
|
68244
|
+
premiumMultiplier: 0.33,
|
|
68245
|
+
tokenInputMultiplier: 0.1,
|
|
68246
|
+
tokenOutputMultiplier: 0.5,
|
|
68247
|
+
cachedInputMultiplier: 0.01,
|
|
68248
|
+
tier: "trivial",
|
|
68249
|
+
available: true
|
|
68250
|
+
},
|
|
68145
68251
|
{
|
|
68146
68252
|
id: "claude-opus-4.5",
|
|
68147
68253
|
displayName: "Claude Opus 4.5",
|
|
68148
68254
|
premiumMultiplier: 15,
|
|
68149
|
-
tokenInputMultiplier:
|
|
68150
|
-
tokenOutputMultiplier:
|
|
68151
|
-
cachedInputMultiplier:
|
|
68255
|
+
tokenInputMultiplier: 0.5,
|
|
68256
|
+
tokenOutputMultiplier: 2.5,
|
|
68257
|
+
cachedInputMultiplier: 0.05,
|
|
68152
68258
|
tier: "premium",
|
|
68153
68259
|
available: true
|
|
68154
68260
|
},
|
|
@@ -68156,9 +68262,9 @@ var init_seed = __esm({
|
|
|
68156
68262
|
id: "claude-opus-4.6",
|
|
68157
68263
|
displayName: "Claude Opus 4.6",
|
|
68158
68264
|
premiumMultiplier: 27,
|
|
68159
|
-
tokenInputMultiplier:
|
|
68160
|
-
tokenOutputMultiplier:
|
|
68161
|
-
cachedInputMultiplier:
|
|
68265
|
+
tokenInputMultiplier: 0.5,
|
|
68266
|
+
tokenOutputMultiplier: 2.5,
|
|
68267
|
+
cachedInputMultiplier: 0.05,
|
|
68162
68268
|
tier: "ultra",
|
|
68163
68269
|
available: true
|
|
68164
68270
|
},
|
|
@@ -68166,21 +68272,81 @@ var init_seed = __esm({
|
|
|
68166
68272
|
id: "claude-opus-4.7",
|
|
68167
68273
|
displayName: "Claude Opus 4.7",
|
|
68168
68274
|
premiumMultiplier: 27,
|
|
68169
|
-
tokenInputMultiplier:
|
|
68170
|
-
tokenOutputMultiplier:
|
|
68171
|
-
cachedInputMultiplier:
|
|
68275
|
+
tokenInputMultiplier: 0.5,
|
|
68276
|
+
tokenOutputMultiplier: 2.5,
|
|
68277
|
+
cachedInputMultiplier: 0.05,
|
|
68172
68278
|
tier: "ultra",
|
|
68173
68279
|
available: true
|
|
68174
68280
|
},
|
|
68175
68281
|
{
|
|
68176
|
-
id: "
|
|
68177
|
-
displayName: "
|
|
68178
|
-
premiumMultiplier:
|
|
68179
|
-
tokenInputMultiplier:
|
|
68180
|
-
tokenOutputMultiplier:
|
|
68181
|
-
cachedInputMultiplier:
|
|
68282
|
+
id: "claude-opus-4.8",
|
|
68283
|
+
displayName: "Claude Opus 4.8",
|
|
68284
|
+
premiumMultiplier: 27,
|
|
68285
|
+
tokenInputMultiplier: 0.5,
|
|
68286
|
+
tokenOutputMultiplier: 2.5,
|
|
68287
|
+
cachedInputMultiplier: 0.05,
|
|
68182
68288
|
tier: "ultra",
|
|
68183
68289
|
available: true
|
|
68290
|
+
},
|
|
68291
|
+
{
|
|
68292
|
+
id: "gemini-2.5-pro",
|
|
68293
|
+
displayName: "Gemini 2.5 Pro",
|
|
68294
|
+
premiumMultiplier: 1,
|
|
68295
|
+
tokenInputMultiplier: 0.125,
|
|
68296
|
+
tokenOutputMultiplier: 1,
|
|
68297
|
+
cachedInputMultiplier: 0.0125,
|
|
68298
|
+
tier: "fast",
|
|
68299
|
+
available: true
|
|
68300
|
+
},
|
|
68301
|
+
{
|
|
68302
|
+
id: "gemini-3-flash",
|
|
68303
|
+
displayName: "Gemini 3 Flash",
|
|
68304
|
+
premiumMultiplier: 0.33,
|
|
68305
|
+
tokenInputMultiplier: 0.05,
|
|
68306
|
+
tokenOutputMultiplier: 0.3,
|
|
68307
|
+
cachedInputMultiplier: 5e-3,
|
|
68308
|
+
tier: "trivial",
|
|
68309
|
+
available: true
|
|
68310
|
+
},
|
|
68311
|
+
{
|
|
68312
|
+
id: "gemini-3.1-pro",
|
|
68313
|
+
displayName: "Gemini 3.1 Pro",
|
|
68314
|
+
premiumMultiplier: 6,
|
|
68315
|
+
tokenInputMultiplier: 0.2,
|
|
68316
|
+
tokenOutputMultiplier: 1.2,
|
|
68317
|
+
cachedInputMultiplier: 0.02,
|
|
68318
|
+
tier: "premium",
|
|
68319
|
+
available: true
|
|
68320
|
+
},
|
|
68321
|
+
{
|
|
68322
|
+
id: "gemini-3.5-flash",
|
|
68323
|
+
displayName: "Gemini 3.5 Flash",
|
|
68324
|
+
premiumMultiplier: 14,
|
|
68325
|
+
tokenInputMultiplier: 0.15,
|
|
68326
|
+
tokenOutputMultiplier: 0.9,
|
|
68327
|
+
cachedInputMultiplier: 0.015,
|
|
68328
|
+
tier: "premium",
|
|
68329
|
+
available: true
|
|
68330
|
+
},
|
|
68331
|
+
{
|
|
68332
|
+
id: "raptor-mini",
|
|
68333
|
+
displayName: "Raptor mini",
|
|
68334
|
+
premiumMultiplier: 0.33,
|
|
68335
|
+
tokenInputMultiplier: 0.025,
|
|
68336
|
+
tokenOutputMultiplier: 0.2,
|
|
68337
|
+
cachedInputMultiplier: 25e-4,
|
|
68338
|
+
tier: "trivial",
|
|
68339
|
+
available: true
|
|
68340
|
+
},
|
|
68341
|
+
{
|
|
68342
|
+
id: "mai-code-1-flash",
|
|
68343
|
+
displayName: "MAI-Code-1-Flash",
|
|
68344
|
+
premiumMultiplier: 0.33,
|
|
68345
|
+
tokenInputMultiplier: 0.075,
|
|
68346
|
+
tokenOutputMultiplier: 0.45,
|
|
68347
|
+
cachedInputMultiplier: 75e-4,
|
|
68348
|
+
tier: "trivial",
|
|
68349
|
+
available: true
|
|
68184
68350
|
}
|
|
68185
68351
|
];
|
|
68186
68352
|
}
|
|
@@ -68283,19 +68449,51 @@ async function scrapePremiumPricingIntoMap(modelMap, result, logger2) {
|
|
|
68283
68449
|
logger2?.warn(`Premium pricing scrape failed: ${msg}`);
|
|
68284
68450
|
}
|
|
68285
68451
|
}
|
|
68452
|
+
async function scrapeCopilotPricingIntoMap(modelMap, result, logger2) {
|
|
68453
|
+
try {
|
|
68454
|
+
const copilotPricing = await scrapeCopilotPricing();
|
|
68455
|
+
result.copilotPricingScraped = true;
|
|
68456
|
+
for (const cp of copilotPricing) {
|
|
68457
|
+
const key = normalizeModelName(cp.modelName);
|
|
68458
|
+
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
68459
|
+
if (existing) {
|
|
68460
|
+
existing.tokenInputMultiplier = cp.inputMultiplier;
|
|
68461
|
+
existing.tokenOutputMultiplier = cp.outputMultiplier;
|
|
68462
|
+
if (cp.cachedInputMultiplier !== null) {
|
|
68463
|
+
existing.cachedInputMultiplier = cp.cachedInputMultiplier;
|
|
68464
|
+
}
|
|
68465
|
+
} else {
|
|
68466
|
+
modelMap.set(key, {
|
|
68467
|
+
id: key,
|
|
68468
|
+
displayName: cp.modelName,
|
|
68469
|
+
tokenInputMultiplier: cp.inputMultiplier,
|
|
68470
|
+
tokenOutputMultiplier: cp.outputMultiplier,
|
|
68471
|
+
cachedInputMultiplier: cp.cachedInputMultiplier,
|
|
68472
|
+
available: true
|
|
68473
|
+
});
|
|
68474
|
+
}
|
|
68475
|
+
}
|
|
68476
|
+
} catch (error51) {
|
|
68477
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
68478
|
+
result.errors.push(`Copilot pricing scrape failed: ${msg}`);
|
|
68479
|
+
logger2?.warn(`Copilot pricing scrape failed: ${msg}`);
|
|
68480
|
+
}
|
|
68481
|
+
}
|
|
68286
68482
|
async function refreshModelPricing(logger2) {
|
|
68287
68483
|
const result = {
|
|
68288
68484
|
modelsUpdated: 0,
|
|
68289
68485
|
catalogFetched: false,
|
|
68290
68486
|
tokenPricingScraped: false,
|
|
68291
68487
|
premiumPricingScraped: false,
|
|
68488
|
+
copilotPricingScraped: false,
|
|
68292
68489
|
errors: []
|
|
68293
68490
|
};
|
|
68294
68491
|
const modelMap = /* @__PURE__ */ new Map();
|
|
68295
68492
|
await fetchCatalogIntoMap(modelMap, result, logger2);
|
|
68296
68493
|
await scrapeTokenPricingIntoMap(modelMap, result, logger2);
|
|
68494
|
+
await scrapeCopilotPricingIntoMap(modelMap, result, logger2);
|
|
68297
68495
|
await scrapePremiumPricingIntoMap(modelMap, result, logger2);
|
|
68298
|
-
if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped) {
|
|
68496
|
+
if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped && !result.copilotPricingScraped) {
|
|
68299
68497
|
logger2?.warn("All pricing sources failed, using seed data");
|
|
68300
68498
|
await seedFromFallback();
|
|
68301
68499
|
result.modelsUpdated = SEED_MODELS.length;
|
|
@@ -68441,9 +68639,10 @@ function buildSystemPrompt(options2) {
|
|
|
68441
68639
|
const sections = [
|
|
68442
68640
|
"You are Io, a helpful AI orchestrator companion for IO v4.",
|
|
68443
68641
|
"You coordinate work across squads, wiki knowledge, installed skills, and direct execution tools.",
|
|
68444
|
-
"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.",
|
|
68642
|
+
"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 or create a squad for ongoing project work.",
|
|
68445
68643
|
"Be practical, concise, and execution-oriented. Use tools when they help you produce a more accurate or durable result.",
|
|
68446
|
-
"## 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-
|
|
68644
|
+
"## 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 for one-off tasks.\n- For ongoing project work where no squad exists, create one with `create_squad` and then add members with `add_squad_member`.\n- Use the wiki to remember important information and recover prior context.\n- Use installed skills when they are relevant to the current request.",
|
|
68645
|
+
"## Squad Creation Guidelines\n- Use `analyze_repo` first to understand the repository's tech stack and structure.\n- Always add a `team-lead` member (coordinates planning, task assignment, and technical review) and a `qa` member (validates diffs, tests, and objective completion).\n- Add additional role-specific members based on the repository analysis (e.g. senior-frontend-engineer, senior-backend-engineer, senior-platform-engineer).\n- Write detailed system prompts for each member that define their expertise, responsibilities, and behavioral guidelines.\n- Use `remove_squad_member` and `add_squad_member` to restructure existing squads when the user requests changes.",
|
|
68447
68646
|
`## Squad Roster
|
|
68448
68647
|
${formatSquadRoster(options2.squads)}`,
|
|
68449
68648
|
formatOptionalSection("## Conversation Summary", options2.conversationSummary ?? ""),
|
|
@@ -69842,22 +70041,7 @@ var init_instance_context = __esm({
|
|
|
69842
70041
|
});
|
|
69843
70042
|
|
|
69844
70043
|
// packages/daemon/src/squad/roles.ts
|
|
69845
|
-
|
|
69846
|
-
const normalizedRole = role.trim() || "specialist";
|
|
69847
|
-
return `You are the ${normalizedRole} for the IO v4 daemon squad system.
|
|
69848
|
-
|
|
69849
|
-
${ROLE_GUIDELINES}
|
|
69850
|
-
|
|
69851
|
-
Role expectations:
|
|
69852
|
-
- Own work that naturally fits the ${normalizedRole} discipline.
|
|
69853
|
-
- Make changes that align with the repository's existing architecture, conventions, and tooling.
|
|
69854
|
-
- Collaborate with the Team Lead and QA by leaving behind clear implementation notes and validation evidence.
|
|
69855
|
-
- Escalate blockers or missing context early.
|
|
69856
|
-
|
|
69857
|
-
Repository context:
|
|
69858
|
-
${repoContext}`;
|
|
69859
|
-
}
|
|
69860
|
-
var ROLE_GUIDELINES, TEAM_LEAD_PROMPT, QA_PROMPT, ROLE_GENERATION_PROMPT;
|
|
70044
|
+
var ROLE_GUIDELINES, TEAM_LEAD_PROMPT, QA_PROMPT;
|
|
69861
70045
|
var init_roles = __esm({
|
|
69862
70046
|
"packages/daemon/src/squad/roles.ts"() {
|
|
69863
70047
|
"use strict";
|
|
@@ -69903,29 +70087,6 @@ Review rules:
|
|
|
69903
70087
|
- If approving, summarize why the work is acceptable.
|
|
69904
70088
|
- If rejecting, include concrete fixes or follow-up actions.
|
|
69905
70089
|
- Do not approve work that lacks evidence for important behavior changes.`;
|
|
69906
|
-
ROLE_GENERATION_PROMPT = `You are staffing an autonomous software squad for a GitHub repository.
|
|
69907
|
-
|
|
69908
|
-
Given repository analysis describing languages, frameworks, file structure, architectural patterns, and operational concerns, propose the smallest effective team that can deliver objectives safely.
|
|
69909
|
-
|
|
69910
|
-
Requirements:
|
|
69911
|
-
- Mandatory roles Team Lead and QA must always exist \u2014 do NOT include them in your response.
|
|
69912
|
-
- Suggest only additional roles that are clearly justified by the repository analysis.
|
|
69913
|
-
- 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".
|
|
69914
|
-
- Do NOT use generic names like "Frontend Engineer" or "Backend Engineer". Be specific about the technology and domain.
|
|
69915
|
-
- Each role must have a short human-readable name and a concise description of responsibilities.
|
|
69916
|
-
- Avoid duplicate or overlapping roles.
|
|
69917
|
-
- Optimize for implementation, verification, and maintainability.
|
|
69918
|
-
|
|
69919
|
-
Return strict JSON in this shape:
|
|
69920
|
-
{
|
|
69921
|
-
"roles": [
|
|
69922
|
-
{
|
|
69923
|
-
"role": "principal-react-engineer",
|
|
69924
|
-
"name": "Principal React Engineer",
|
|
69925
|
-
"description": "Owns UI implementation, client state management, and browser-facing integration tests."
|
|
69926
|
-
}
|
|
69927
|
-
]
|
|
69928
|
-
}`;
|
|
69929
70090
|
}
|
|
69930
70091
|
});
|
|
69931
70092
|
|
|
@@ -70732,8 +70893,19 @@ var init_runner = __esm({
|
|
|
70732
70893
|
}
|
|
70733
70894
|
});
|
|
70734
70895
|
|
|
70735
|
-
// packages/daemon/src/squad
|
|
70736
|
-
import {
|
|
70896
|
+
// packages/daemon/src/orchestrator/tools/squad.ts
|
|
70897
|
+
import { exec as exec8 } from "node:child_process";
|
|
70898
|
+
import { mkdir as mkdir10, readFile as readFile9, readdir as readdir6, stat as stat4 } from "node:fs/promises";
|
|
70899
|
+
import { join as join15 } from "node:path";
|
|
70900
|
+
import { promisify as promisify8 } from "node:util";
|
|
70901
|
+
async function pathExists2(path) {
|
|
70902
|
+
try {
|
|
70903
|
+
await stat4(path);
|
|
70904
|
+
return true;
|
|
70905
|
+
} catch {
|
|
70906
|
+
return false;
|
|
70907
|
+
}
|
|
70908
|
+
}
|
|
70737
70909
|
function parseRepoUrl(repoUrl) {
|
|
70738
70910
|
const trimmed = repoUrl.trim();
|
|
70739
70911
|
const sshMatch = trimmed.match(/^git@[^:]+:([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
@@ -70748,248 +70920,9 @@ function parseRepoUrl(repoUrl) {
|
|
|
70748
70920
|
}
|
|
70749
70921
|
return { owner: segments[0], name: segments[1] };
|
|
70750
70922
|
}
|
|
70751
|
-
function slugifyRole(role) {
|
|
70752
|
-
return role.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
70753
|
-
}
|
|
70754
70923
|
function titleCase(value) {
|
|
70755
70924
|
return value.split(/[-_\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
70756
70925
|
}
|
|
70757
|
-
function extractJsonObject4(content) {
|
|
70758
|
-
const fenced = content.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
70759
|
-
if (fenced?.[1]) {
|
|
70760
|
-
return fenced[1].trim();
|
|
70761
|
-
}
|
|
70762
|
-
const start = content.indexOf("{");
|
|
70763
|
-
const end = content.lastIndexOf("}");
|
|
70764
|
-
return start >= 0 && end > start ? content.slice(start, end + 1) : null;
|
|
70765
|
-
}
|
|
70766
|
-
function detectRolesFromAnalysis(repoAnalysis) {
|
|
70767
|
-
const analysis = repoAnalysis.toLowerCase();
|
|
70768
|
-
const roles = [];
|
|
70769
|
-
if (/(react|next|vue|angular|frontend|ui|xaml)/.test(analysis)) {
|
|
70770
|
-
roles.push({
|
|
70771
|
-
role: "senior-frontend-engineer",
|
|
70772
|
-
name: "Senior Frontend Engineer",
|
|
70773
|
-
description: "Implements user-facing experiences, UI state, and presentation-layer changes."
|
|
70774
|
-
});
|
|
70775
|
-
}
|
|
70776
|
-
if (/(node|express|api|backend|server|daemon|database|sql|postgres|sqlite)/.test(analysis)) {
|
|
70777
|
-
roles.push({
|
|
70778
|
-
role: "senior-backend-engineer",
|
|
70779
|
-
name: "Senior Backend Engineer",
|
|
70780
|
-
description: "Implements server-side logic, data access, integrations, and execution flow changes."
|
|
70781
|
-
});
|
|
70782
|
-
}
|
|
70783
|
-
if (/(infra|docker|kubernetes|deploy|ci|cd|workflow|github actions)/.test(analysis)) {
|
|
70784
|
-
roles.push({
|
|
70785
|
-
role: "senior-platform-engineer",
|
|
70786
|
-
name: "Senior Platform Engineer",
|
|
70787
|
-
description: "Owns automation, pipelines, environments, and operational tooling."
|
|
70788
|
-
});
|
|
70789
|
-
}
|
|
70790
|
-
if (/(security|auth|oauth|secret|policy)/.test(analysis)) {
|
|
70791
|
-
roles.push({
|
|
70792
|
-
role: "senior-security-engineer",
|
|
70793
|
-
name: "Senior Security Engineer",
|
|
70794
|
-
description: "Reviews authentication, authorization, secrets handling, and security-sensitive changes."
|
|
70795
|
-
});
|
|
70796
|
-
}
|
|
70797
|
-
return roles;
|
|
70798
|
-
}
|
|
70799
|
-
function dedupeRoles(roles) {
|
|
70800
|
-
const seen = /* @__PURE__ */ new Set();
|
|
70801
|
-
const output2 = [];
|
|
70802
|
-
for (const role of roles) {
|
|
70803
|
-
const normalizedRole = slugifyRole(role.role);
|
|
70804
|
-
if (!normalizedRole || seen.has(normalizedRole) || MANDATORY_ROLES.includes(normalizedRole)) {
|
|
70805
|
-
continue;
|
|
70806
|
-
}
|
|
70807
|
-
seen.add(normalizedRole);
|
|
70808
|
-
output2.push({
|
|
70809
|
-
role: normalizedRole,
|
|
70810
|
-
name: role.name?.trim() || titleCase(normalizedRole),
|
|
70811
|
-
description: role.description?.trim() || `${titleCase(normalizedRole)} responsibilities`
|
|
70812
|
-
});
|
|
70813
|
-
}
|
|
70814
|
-
return output2;
|
|
70815
|
-
}
|
|
70816
|
-
function buildMandatoryRoles() {
|
|
70817
|
-
return [
|
|
70818
|
-
{
|
|
70819
|
-
role: "team-lead",
|
|
70820
|
-
name: "Team Lead",
|
|
70821
|
-
description: "Coordinates planning, task assignment, sequencing, and final technical review."
|
|
70822
|
-
},
|
|
70823
|
-
{
|
|
70824
|
-
role: "qa",
|
|
70825
|
-
name: "QA Reviewer",
|
|
70826
|
-
description: "Validates diffs, test evidence, and objective completion before delivery."
|
|
70827
|
-
}
|
|
70828
|
-
];
|
|
70829
|
-
}
|
|
70830
|
-
function modelForRole(role) {
|
|
70831
|
-
const normalized = slugifyRole(role);
|
|
70832
|
-
if (normalized === "team-lead") {
|
|
70833
|
-
return DEFAULT_MODEL;
|
|
70834
|
-
}
|
|
70835
|
-
return DEFAULT_MODEL;
|
|
70836
|
-
}
|
|
70837
|
-
function systemPromptForRole(role, repoContext) {
|
|
70838
|
-
const normalized = slugifyRole(role);
|
|
70839
|
-
if (normalized === "team-lead") {
|
|
70840
|
-
return `${TEAM_LEAD_PROMPT}
|
|
70841
|
-
|
|
70842
|
-
Repository context:
|
|
70843
|
-
${repoContext}`;
|
|
70844
|
-
}
|
|
70845
|
-
if (normalized === "qa") {
|
|
70846
|
-
return `${QA_PROMPT}
|
|
70847
|
-
|
|
70848
|
-
Repository context:
|
|
70849
|
-
${repoContext}`;
|
|
70850
|
-
}
|
|
70851
|
-
return generateRolePrompt(normalized, repoContext);
|
|
70852
|
-
}
|
|
70853
|
-
async function proposeSquadComposition(repoUrl, repoAnalysis, context) {
|
|
70854
|
-
const mandatory = buildMandatoryRoles();
|
|
70855
|
-
const fallbackAdditional = dedupeRoles(detectRolesFromAnalysis(repoAnalysis));
|
|
70856
|
-
const contextSection = context ? `
|
|
70857
|
-
|
|
70858
|
-
Additional context from user:
|
|
70859
|
-
${context}` : "";
|
|
70860
|
-
const prompt = `Repository URL: ${repoUrl}
|
|
70861
|
-
|
|
70862
|
-
Repository analysis:
|
|
70863
|
-
${repoAnalysis}${contextSection}
|
|
70864
|
-
|
|
70865
|
-
${ROLE_GENERATION_PROMPT}`;
|
|
70866
|
-
let client2 = null;
|
|
70867
|
-
try {
|
|
70868
|
-
client2 = new CopilotClient6();
|
|
70869
|
-
await client2.start();
|
|
70870
|
-
const session = await client2.createSession({
|
|
70871
|
-
model: DEFAULT_MODEL,
|
|
70872
|
-
onPermissionRequest: approveAll6,
|
|
70873
|
-
systemMessage: {
|
|
70874
|
-
content: ROLE_GENERATION_PROMPT
|
|
70875
|
-
}
|
|
70876
|
-
});
|
|
70877
|
-
try {
|
|
70878
|
-
const response = await session.sendAndWait({ prompt }, 6e4);
|
|
70879
|
-
const content = response?.data.content?.trim() ?? "";
|
|
70880
|
-
const json2 = extractJsonObject4(content);
|
|
70881
|
-
if (!json2) {
|
|
70882
|
-
return { roles: [...mandatory, ...fallbackAdditional] };
|
|
70883
|
-
}
|
|
70884
|
-
const parsed = JSON.parse(json2);
|
|
70885
|
-
return {
|
|
70886
|
-
roles: [...mandatory, ...dedupeRoles(parsed.roles ?? fallbackAdditional)]
|
|
70887
|
-
};
|
|
70888
|
-
} finally {
|
|
70889
|
-
await session.disconnect().catch(() => void 0);
|
|
70890
|
-
}
|
|
70891
|
-
} catch {
|
|
70892
|
-
return { roles: [...mandatory, ...fallbackAdditional] };
|
|
70893
|
-
} finally {
|
|
70894
|
-
if (client2) {
|
|
70895
|
-
await client2.stop().catch(() => []);
|
|
70896
|
-
}
|
|
70897
|
-
}
|
|
70898
|
-
}
|
|
70899
|
-
async function hireSquad(repoUrl, composition, config2) {
|
|
70900
|
-
const parsedRepo = parseRepoUrl(repoUrl);
|
|
70901
|
-
const repoContext = `Repository: ${parsedRepo.owner}/${parsedRepo.name}
|
|
70902
|
-
Configured MCP servers: ${config2.mcpServers.join(", ") || "none"}
|
|
70903
|
-
QA max revisions: ${config2.maxRevisions || QA_MAX_REVISIONS}`;
|
|
70904
|
-
const existingSquad = await getSquadByRepo(parsedRepo.owner, parsedRepo.name);
|
|
70905
|
-
const normalizedConfig = {
|
|
70906
|
-
prMode: config2.prMode,
|
|
70907
|
-
mcpServers: [...config2.mcpServers],
|
|
70908
|
-
maxRevisions: config2.maxRevisions || QA_MAX_REVISIONS
|
|
70909
|
-
};
|
|
70910
|
-
const desiredRoles = composition.roles.length > 0 ? composition.roles : buildMandatoryRoles();
|
|
70911
|
-
const membersToCreate = desiredRoles.map((role) => ({
|
|
70912
|
-
role: slugifyRole(role.role),
|
|
70913
|
-
name: role.name?.trim() || titleCase(role.role),
|
|
70914
|
-
description: role.description?.trim() || `${titleCase(role.role)} responsibilities`
|
|
70915
|
-
}));
|
|
70916
|
-
let squadId;
|
|
70917
|
-
if (existingSquad) {
|
|
70918
|
-
const updatedSquad = await updateSquad(existingSquad.id, {
|
|
70919
|
-
name: `${titleCase(parsedRepo.name)} Squad`,
|
|
70920
|
-
config: normalizedConfig,
|
|
70921
|
-
status: existingSquad.status
|
|
70922
|
-
});
|
|
70923
|
-
if (!updatedSquad) {
|
|
70924
|
-
throw new Error(`Unable to update existing squad for ${repoUrl}`);
|
|
70925
|
-
}
|
|
70926
|
-
squadId = updatedSquad.id;
|
|
70927
|
-
const existingMembers = await getMembers(updatedSquad.id);
|
|
70928
|
-
await Promise.all(existingMembers.map((member) => removeMember(member.id)));
|
|
70929
|
-
} else {
|
|
70930
|
-
const squad = await createSquad({
|
|
70931
|
-
name: `${titleCase(parsedRepo.name)} Squad`,
|
|
70932
|
-
repoUrl,
|
|
70933
|
-
repoOwner: parsedRepo.owner,
|
|
70934
|
-
repoName: parsedRepo.name,
|
|
70935
|
-
status: "active",
|
|
70936
|
-
config: normalizedConfig
|
|
70937
|
-
});
|
|
70938
|
-
squadId = squad.id;
|
|
70939
|
-
}
|
|
70940
|
-
const members = [];
|
|
70941
|
-
for (const memberDefinition of membersToCreate) {
|
|
70942
|
-
const member = await addMember(squadId, {
|
|
70943
|
-
role: memberDefinition.role,
|
|
70944
|
-
name: memberDefinition.name,
|
|
70945
|
-
systemPrompt: systemPromptForRole(
|
|
70946
|
-
memberDefinition.role,
|
|
70947
|
-
`${repoContext}
|
|
70948
|
-
Role: ${memberDefinition.description}`
|
|
70949
|
-
),
|
|
70950
|
-
model: modelForRole(memberDefinition.role)
|
|
70951
|
-
});
|
|
70952
|
-
members.push(member);
|
|
70953
|
-
}
|
|
70954
|
-
const fullSquad = await getSquad(squadId);
|
|
70955
|
-
if (!fullSquad) {
|
|
70956
|
-
throw new Error(`Unable to load squad ${squadId} after hiring`);
|
|
70957
|
-
}
|
|
70958
|
-
eventBus.emit(existingSquad ? EVENT_NAMES.SQUAD_UPDATED : EVENT_NAMES.SQUAD_CREATED, {
|
|
70959
|
-
squad: fullSquad
|
|
70960
|
-
});
|
|
70961
|
-
return { squad: fullSquad, members: fullSquad.members };
|
|
70962
|
-
}
|
|
70963
|
-
var init_hiring = __esm({
|
|
70964
|
-
"packages/daemon/src/squad/hiring.ts"() {
|
|
70965
|
-
"use strict";
|
|
70966
|
-
init_dist();
|
|
70967
|
-
init_event_bus();
|
|
70968
|
-
init_store2();
|
|
70969
|
-
init_roles();
|
|
70970
|
-
}
|
|
70971
|
-
});
|
|
70972
|
-
|
|
70973
|
-
// packages/daemon/src/orchestrator/tools/squad.ts
|
|
70974
|
-
import { exec as exec8 } from "node:child_process";
|
|
70975
|
-
import { mkdir as mkdir10, readFile as readFile9, readdir as readdir6, stat as stat4 } from "node:fs/promises";
|
|
70976
|
-
import { join as join15 } from "node:path";
|
|
70977
|
-
import { promisify as promisify8 } from "node:util";
|
|
70978
|
-
async function pathExists2(path) {
|
|
70979
|
-
try {
|
|
70980
|
-
await stat4(path);
|
|
70981
|
-
return true;
|
|
70982
|
-
} catch {
|
|
70983
|
-
return false;
|
|
70984
|
-
}
|
|
70985
|
-
}
|
|
70986
|
-
function getDefaultSquadConfig(_config) {
|
|
70987
|
-
return {
|
|
70988
|
-
prMode: "draft-pr",
|
|
70989
|
-
mcpServers: [],
|
|
70990
|
-
maxRevisions: QA_MAX_REVISIONS
|
|
70991
|
-
};
|
|
70992
|
-
}
|
|
70993
70926
|
async function readManifests(rootPath, rootLabel) {
|
|
70994
70927
|
const lines = [];
|
|
70995
70928
|
for (const manifest of MANIFEST_FILES) {
|
|
@@ -71154,18 +71087,98 @@ async function startAndExecuteInstance(instanceId, squad, objectiveId) {
|
|
|
71154
71087
|
await processQueue2(squad.id);
|
|
71155
71088
|
}
|
|
71156
71089
|
}
|
|
71157
|
-
async function
|
|
71158
|
-
const { repoUrl,
|
|
71159
|
-
const
|
|
71090
|
+
async function handleCreateSquad(rawArgs) {
|
|
71091
|
+
const { repoUrl, name, config: config2 } = createSquadSchema.parse(rawArgs);
|
|
71092
|
+
const parsedRepo = parseRepoUrl(repoUrl);
|
|
71093
|
+
const existingSquad = await getSquadByRepo(parsedRepo.owner, parsedRepo.name);
|
|
71094
|
+
if (existingSquad) {
|
|
71095
|
+
return {
|
|
71096
|
+
message: `A squad already exists for ${parsedRepo.owner}/${parsedRepo.name}.`,
|
|
71097
|
+
squad: existingSquad
|
|
71098
|
+
};
|
|
71099
|
+
}
|
|
71100
|
+
const squadName = name || `${titleCase(parsedRepo.name)} Squad`;
|
|
71101
|
+
const squadConfig = {
|
|
71102
|
+
prMode: config2?.prMode ?? "draft-pr",
|
|
71103
|
+
mcpServers: config2?.mcpServers ?? [],
|
|
71104
|
+
maxRevisions: config2?.maxRevisions ?? QA_MAX_REVISIONS
|
|
71105
|
+
};
|
|
71106
|
+
const squad = await createSquad({
|
|
71107
|
+
name: squadName,
|
|
71160
71108
|
repoUrl,
|
|
71161
|
-
|
|
71162
|
-
|
|
71163
|
-
|
|
71164
|
-
|
|
71109
|
+
repoOwner: parsedRepo.owner,
|
|
71110
|
+
repoName: parsedRepo.name,
|
|
71111
|
+
status: "active",
|
|
71112
|
+
config: squadConfig
|
|
71113
|
+
});
|
|
71114
|
+
eventBus.emit(EVENT_NAMES.SQUAD_CREATED, { squad });
|
|
71115
|
+
return {
|
|
71116
|
+
message: `Squad "${squad.name}" created for ${parsedRepo.owner}/${parsedRepo.name}. Add members with add_squad_member.`,
|
|
71117
|
+
squad
|
|
71118
|
+
};
|
|
71119
|
+
}
|
|
71120
|
+
async function handleAddSquadMember(rawArgs) {
|
|
71121
|
+
const { squadId, role, name, systemPrompt, model } = addSquadMemberSchema.parse(rawArgs);
|
|
71122
|
+
const squad = await getSquad(squadId);
|
|
71123
|
+
if (!squad) {
|
|
71124
|
+
throw new Error(`Squad ${squadId} was not found.`);
|
|
71125
|
+
}
|
|
71126
|
+
const member = await addMember(squadId, {
|
|
71127
|
+
role,
|
|
71128
|
+
name,
|
|
71129
|
+
systemPrompt,
|
|
71130
|
+
model: model ?? null
|
|
71131
|
+
});
|
|
71132
|
+
eventBus.emit(EVENT_NAMES.SQUAD_MEMBER_UPDATED, { squadId, member });
|
|
71165
71133
|
return {
|
|
71166
|
-
message: `
|
|
71167
|
-
|
|
71168
|
-
|
|
71134
|
+
message: `Added member "${member.name}" (${member.role}) to squad "${squad.name}".`,
|
|
71135
|
+
member
|
|
71136
|
+
};
|
|
71137
|
+
}
|
|
71138
|
+
async function handleRemoveSquadMember(rawArgs) {
|
|
71139
|
+
const { squadId, memberId } = removeSquadMemberSchema.parse(rawArgs);
|
|
71140
|
+
const squad = await getSquad(squadId);
|
|
71141
|
+
if (!squad) {
|
|
71142
|
+
throw new Error(`Squad ${squadId} was not found.`);
|
|
71143
|
+
}
|
|
71144
|
+
const member = await getMember(memberId);
|
|
71145
|
+
if (!member || member.squadId !== squadId) {
|
|
71146
|
+
throw new Error(`Member ${memberId} was not found in squad ${squadId}.`);
|
|
71147
|
+
}
|
|
71148
|
+
await removeMember(memberId);
|
|
71149
|
+
eventBus.emit(EVENT_NAMES.SQUAD_UPDATED, { squad });
|
|
71150
|
+
return {
|
|
71151
|
+
message: `Removed member "${member.name}" (${member.role}) from squad "${squad.name}".`,
|
|
71152
|
+
memberId
|
|
71153
|
+
};
|
|
71154
|
+
}
|
|
71155
|
+
async function handleUpdateSquadConfig(rawArgs) {
|
|
71156
|
+
const { squadId, config: config2 } = updateSquadConfigSchema.parse(rawArgs);
|
|
71157
|
+
const squad = await getSquad(squadId);
|
|
71158
|
+
if (!squad) {
|
|
71159
|
+
throw new Error(`Squad ${squadId} was not found.`);
|
|
71160
|
+
}
|
|
71161
|
+
const mergedConfig = {
|
|
71162
|
+
prMode: config2.prMode ?? squad.config.prMode,
|
|
71163
|
+
mcpServers: config2.mcpServers ?? squad.config.mcpServers,
|
|
71164
|
+
maxRevisions: config2.maxRevisions ?? squad.config.maxRevisions
|
|
71165
|
+
};
|
|
71166
|
+
const updated = await updateSquad(squadId, { config: mergedConfig });
|
|
71167
|
+
if (!updated) {
|
|
71168
|
+
throw new Error(`Failed to update squad ${squadId}.`);
|
|
71169
|
+
}
|
|
71170
|
+
eventBus.emit(EVENT_NAMES.SQUAD_UPDATED, { squad: updated });
|
|
71171
|
+
return {
|
|
71172
|
+
message: `Updated config for squad "${updated.name}".`,
|
|
71173
|
+
squad: updated
|
|
71174
|
+
};
|
|
71175
|
+
}
|
|
71176
|
+
async function handleAnalyzeRepo(rawArgs) {
|
|
71177
|
+
const { repoUrl, scanPaths } = analyzeRepoSchema.parse(rawArgs);
|
|
71178
|
+
const analysis = await buildRepoAnalysis(repoUrl, scanPaths);
|
|
71179
|
+
return {
|
|
71180
|
+
message: "Repository analysis complete.",
|
|
71181
|
+
analysis
|
|
71169
71182
|
};
|
|
71170
71183
|
}
|
|
71171
71184
|
async function handleFireSquad(rawArgs) {
|
|
@@ -71234,11 +71247,19 @@ async function handleUpdateSquadMember(rawArgs) {
|
|
|
71234
71247
|
member: updated
|
|
71235
71248
|
};
|
|
71236
71249
|
}
|
|
71237
|
-
function createSquadToolExecutor(
|
|
71250
|
+
function createSquadToolExecutor(_config) {
|
|
71238
71251
|
return async (toolName, rawArgs) => {
|
|
71239
71252
|
switch (toolName) {
|
|
71240
|
-
case "
|
|
71241
|
-
return
|
|
71253
|
+
case "create_squad":
|
|
71254
|
+
return handleCreateSquad(rawArgs);
|
|
71255
|
+
case "add_squad_member":
|
|
71256
|
+
return handleAddSquadMember(rawArgs);
|
|
71257
|
+
case "remove_squad_member":
|
|
71258
|
+
return handleRemoveSquadMember(rawArgs);
|
|
71259
|
+
case "update_squad_config":
|
|
71260
|
+
return handleUpdateSquadConfig(rawArgs);
|
|
71261
|
+
case "analyze_repo":
|
|
71262
|
+
return handleAnalyzeRepo(rawArgs);
|
|
71242
71263
|
case "fire_squad":
|
|
71243
71264
|
return handleFireSquad(rawArgs);
|
|
71244
71265
|
case "list_squads": {
|
|
@@ -71277,7 +71298,7 @@ function createSquadToolExecutor(config2) {
|
|
|
71277
71298
|
}
|
|
71278
71299
|
};
|
|
71279
71300
|
}
|
|
71280
|
-
var execAsync8,
|
|
71301
|
+
var execAsync8, createSquadSchema, addSquadMemberSchema, removeSquadMemberSchema, updateSquadConfigSchema, analyzeRepoSchema, squadIdSchema, delegateToSquadSchema, renameSquadSchema, updateSquadMemberSchema, squadToolDefinitions, MANIFEST_FILES, SRC_DIR_NAMES, README_CANDIDATES2;
|
|
71281
71302
|
var init_squad2 = __esm({
|
|
71282
71303
|
"packages/daemon/src/orchestrator/tools/squad.ts"() {
|
|
71283
71304
|
"use strict";
|
|
@@ -71287,17 +71308,43 @@ var init_squad2 = __esm({
|
|
|
71287
71308
|
init_event_bus();
|
|
71288
71309
|
init_instances2();
|
|
71289
71310
|
init_runner();
|
|
71290
|
-
init_hiring();
|
|
71291
71311
|
init_manager2();
|
|
71292
71312
|
init_store2();
|
|
71293
71313
|
execAsync8 = promisify8(exec8);
|
|
71294
|
-
|
|
71295
|
-
repoUrl: external_exports.string().trim().min(1).describe("The repository URL to
|
|
71296
|
-
|
|
71297
|
-
|
|
71314
|
+
createSquadSchema = external_exports.object({
|
|
71315
|
+
repoUrl: external_exports.string().trim().min(1).describe("The repository URL to create a squad for"),
|
|
71316
|
+
name: external_exports.string().trim().optional().describe("Squad name. Defaults to '<RepoName> Squad' if not provided."),
|
|
71317
|
+
config: external_exports.object({
|
|
71318
|
+
prMode: external_exports.enum(["draft-pr", "branch-only", "ready-pr", "auto-merge"]).optional().describe("PR creation mode. Defaults to 'draft-pr'."),
|
|
71319
|
+
mcpServers: external_exports.array(external_exports.string()).optional().describe("MCP server URLs to configure for the squad."),
|
|
71320
|
+
maxRevisions: external_exports.number().optional().describe("Max QA revision cycles. Defaults to system default.")
|
|
71321
|
+
}).optional().describe("Squad configuration. All fields are optional with sensible defaults.")
|
|
71322
|
+
});
|
|
71323
|
+
addSquadMemberSchema = external_exports.object({
|
|
71324
|
+
squadId: external_exports.string().trim().min(1),
|
|
71325
|
+
role: external_exports.string().trim().min(1).describe("Slug-style role identifier (e.g. 'senior-frontend-engineer', 'team-lead')"),
|
|
71326
|
+
name: external_exports.string().trim().min(1).describe("Display name for the member (e.g. 'Senior Frontend Engineer')"),
|
|
71327
|
+
systemPrompt: external_exports.string().min(1).describe(
|
|
71328
|
+
"The system prompt defining this member's expertise, responsibilities, and behavior."
|
|
71298
71329
|
),
|
|
71330
|
+
model: external_exports.string().optional().describe("Model override for this member. Uses squad default if not provided.")
|
|
71331
|
+
});
|
|
71332
|
+
removeSquadMemberSchema = external_exports.object({
|
|
71333
|
+
squadId: external_exports.string().trim().min(1),
|
|
71334
|
+
memberId: external_exports.string().trim().min(1)
|
|
71335
|
+
});
|
|
71336
|
+
updateSquadConfigSchema = external_exports.object({
|
|
71337
|
+
squadId: external_exports.string().trim().min(1),
|
|
71338
|
+
config: external_exports.object({
|
|
71339
|
+
prMode: external_exports.enum(["draft-pr", "branch-only", "ready-pr", "auto-merge"]).optional(),
|
|
71340
|
+
mcpServers: external_exports.array(external_exports.string()).optional(),
|
|
71341
|
+
maxRevisions: external_exports.number().optional()
|
|
71342
|
+
}).describe("Partial config fields to update. Only provided fields are changed.")
|
|
71343
|
+
});
|
|
71344
|
+
analyzeRepoSchema = external_exports.object({
|
|
71345
|
+
repoUrl: external_exports.string().trim().min(1).describe("The repository URL to analyze"),
|
|
71299
71346
|
scanPaths: external_exports.array(external_exports.string()).optional().describe(
|
|
71300
|
-
"Relative paths within the repository to focus the analysis on. When provided, only these directories are scanned
|
|
71347
|
+
"Relative paths within the repository to focus the analysis on. When provided, only these directories are scanned instead of the entire repo."
|
|
71301
71348
|
)
|
|
71302
71349
|
});
|
|
71303
71350
|
squadIdSchema = external_exports.object({
|
|
@@ -71320,9 +71367,33 @@ var init_squad2 = __esm({
|
|
|
71320
71367
|
});
|
|
71321
71368
|
squadToolDefinitions = [
|
|
71322
71369
|
{
|
|
71323
|
-
name: "
|
|
71324
|
-
description: "
|
|
71325
|
-
parameters:
|
|
71370
|
+
name: "create_squad",
|
|
71371
|
+
description: "Create a new squad for a repository. After creating, use add_squad_member to populate it with agents. Always include team-lead and qa roles.",
|
|
71372
|
+
parameters: createSquadSchema,
|
|
71373
|
+
skipPermission: true
|
|
71374
|
+
},
|
|
71375
|
+
{
|
|
71376
|
+
name: "add_squad_member",
|
|
71377
|
+
description: "Add a member (agent) to an existing squad. Provide a role slug, display name, and a detailed system prompt defining the agent's expertise and responsibilities.",
|
|
71378
|
+
parameters: addSquadMemberSchema,
|
|
71379
|
+
skipPermission: true
|
|
71380
|
+
},
|
|
71381
|
+
{
|
|
71382
|
+
name: "remove_squad_member",
|
|
71383
|
+
description: "Remove a member from a squad by their ID.",
|
|
71384
|
+
parameters: removeSquadMemberSchema,
|
|
71385
|
+
skipPermission: true
|
|
71386
|
+
},
|
|
71387
|
+
{
|
|
71388
|
+
name: "update_squad_config",
|
|
71389
|
+
description: "Update a squad's configuration (prMode, mcpServers, maxRevisions).",
|
|
71390
|
+
parameters: updateSquadConfigSchema,
|
|
71391
|
+
skipPermission: true
|
|
71392
|
+
},
|
|
71393
|
+
{
|
|
71394
|
+
name: "analyze_repo",
|
|
71395
|
+
description: "Analyze a repository's structure and tech stack. Returns information about frameworks, languages, directory layout, and config files. Use this before creating a squad to inform role decisions.",
|
|
71396
|
+
parameters: analyzeRepoSchema,
|
|
71326
71397
|
skipPermission: true
|
|
71327
71398
|
},
|
|
71328
71399
|
{
|