@jmanuelcorral/openteam 0.16.3 → 0.17.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.
- package/.opencode/openteam.example.json +10 -3
- package/README.es.md +4 -3
- package/README.md +4 -3
- package/dist/certificates/graph-release-certificate.json +2 -2
- package/dist/certificates/graph-shadow-certificate.json +2 -2
- package/dist/cli/setupAdapters.d.ts.map +1 -1
- package/dist/cli.js +349 -54
- package/dist/commands/doctor.d.ts +12 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/setup.d.ts +72 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/config/errors.d.ts.map +1 -1
- package/dist/config/graphFeatureGate.d.ts +10 -0
- package/dist/config/graphFeatureGate.d.ts.map +1 -1
- package/dist/config/resolve.d.ts +3 -3
- package/dist/config/schema.d.ts +10 -1
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +259 -53
- package/dist/local/lemonade.d.ts +5 -1
- package/dist/local/lemonade.d.ts.map +1 -1
- package/dist/local/slotPool.d.ts +58 -0
- package/dist/local/slotPool.d.ts.map +1 -0
- package/dist/local/types.d.ts +19 -0
- package/dist/local/types.d.ts.map +1 -1
- package/dist/orchestrator/coordinator.d.ts.map +1 -1
- package/dist/orchestrator/roles.d.ts +28 -0
- package/dist/orchestrator/roles.d.ts.map +1 -1
- package/dist/orchestrator/runtimeSelection.d.ts +7 -1
- package/dist/orchestrator/runtimeSelection.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ import { randomBytes } from "node:crypto";
|
|
|
19
19
|
|
|
20
20
|
// src/config/resolve.ts
|
|
21
21
|
function resolveGraphConfig(config, source) {
|
|
22
|
-
const configuredMode = config.graph?.mode ?? "
|
|
22
|
+
const configuredMode = config.graph?.mode ?? "active";
|
|
23
23
|
const killSwitch = config.graph?.killSwitch ?? false;
|
|
24
24
|
const effectiveMode = killSwitch ? "off" : configuredMode;
|
|
25
25
|
return {
|
|
@@ -129,7 +129,8 @@ var AgentRoleProfileSchema = z.object({
|
|
|
129
129
|
localFirst: z.boolean(),
|
|
130
130
|
requiresLocalRuntime: z.boolean().optional(),
|
|
131
131
|
localRuntimes: z.array(z.string().min(1)).optional(),
|
|
132
|
-
localModels: z.record(z.string().min(1), z.string().min(1)).optional()
|
|
132
|
+
localModels: z.record(z.string().min(1), z.string().min(1)).optional(),
|
|
133
|
+
localModelClass: z.enum(["thinking", "coder"]).optional()
|
|
133
134
|
}).strict();
|
|
134
135
|
var TEAM_ROLES = {
|
|
135
136
|
architect: {
|
|
@@ -140,7 +141,8 @@ var TEAM_ROLES = {
|
|
|
140
141
|
minCodeQualityTier: 5,
|
|
141
142
|
needsToolCallingByDefault: true,
|
|
142
143
|
preferredFrontierModels: ["auto"],
|
|
143
|
-
localFirst: false
|
|
144
|
+
localFirst: false,
|
|
145
|
+
localModelClass: "thinking"
|
|
144
146
|
},
|
|
145
147
|
integration: {
|
|
146
148
|
roleID: "integration",
|
|
@@ -150,7 +152,8 @@ var TEAM_ROLES = {
|
|
|
150
152
|
minCodeQualityTier: 4,
|
|
151
153
|
needsToolCallingByDefault: true,
|
|
152
154
|
preferredFrontierModels: ["auto"],
|
|
153
|
-
localFirst: false
|
|
155
|
+
localFirst: false,
|
|
156
|
+
localModelClass: "coder"
|
|
154
157
|
},
|
|
155
158
|
"local-runtime": {
|
|
156
159
|
roleID: "local-runtime",
|
|
@@ -160,7 +163,8 @@ var TEAM_ROLES = {
|
|
|
160
163
|
minCodeQualityTier: 4,
|
|
161
164
|
needsToolCallingByDefault: true,
|
|
162
165
|
preferredFrontierModels: ["auto"],
|
|
163
|
-
localFirst: true
|
|
166
|
+
localFirst: true,
|
|
167
|
+
localModelClass: "coder"
|
|
164
168
|
},
|
|
165
169
|
"routing-cost": {
|
|
166
170
|
roleID: "routing-cost",
|
|
@@ -170,7 +174,8 @@ var TEAM_ROLES = {
|
|
|
170
174
|
minCodeQualityTier: 4,
|
|
171
175
|
needsToolCallingByDefault: false,
|
|
172
176
|
preferredFrontierModels: ["auto"],
|
|
173
|
-
localFirst: true
|
|
177
|
+
localFirst: true,
|
|
178
|
+
localModelClass: "thinking"
|
|
174
179
|
},
|
|
175
180
|
scribe: {
|
|
176
181
|
roleID: "scribe",
|
|
@@ -203,7 +208,8 @@ var TEAM_ROLES = {
|
|
|
203
208
|
needsToolCallingByDefault: false,
|
|
204
209
|
preferredFrontierModels: [],
|
|
205
210
|
localFirst: true,
|
|
206
|
-
requiresLocalRuntime: true
|
|
211
|
+
requiresLocalRuntime: true,
|
|
212
|
+
localModelClass: "thinking"
|
|
207
213
|
},
|
|
208
214
|
tester: {
|
|
209
215
|
roleID: "tester",
|
|
@@ -213,7 +219,8 @@ var TEAM_ROLES = {
|
|
|
213
219
|
minCodeQualityTier: 3,
|
|
214
220
|
needsToolCallingByDefault: true,
|
|
215
221
|
preferredFrontierModels: ["auto"],
|
|
216
|
-
localFirst: true
|
|
222
|
+
localFirst: true,
|
|
223
|
+
localModelClass: "coder"
|
|
217
224
|
},
|
|
218
225
|
reviewer: {
|
|
219
226
|
roleID: "reviewer",
|
|
@@ -223,7 +230,8 @@ var TEAM_ROLES = {
|
|
|
223
230
|
minCodeQualityTier: 5,
|
|
224
231
|
needsToolCallingByDefault: false,
|
|
225
232
|
preferredFrontierModels: ["auto"],
|
|
226
|
-
localFirst: true
|
|
233
|
+
localFirst: true,
|
|
234
|
+
localModelClass: "coder"
|
|
227
235
|
}
|
|
228
236
|
};
|
|
229
237
|
var LEGACY_TEAM_ROLE_ALIASES = {
|
|
@@ -326,11 +334,11 @@ var GraphEngineSchema = z2.enum(["builtin", "langgraph"]);
|
|
|
326
334
|
var DEFAULT_GRAPH_JOURNAL_ROOT = ".opencode/openteam-local/graph/runs";
|
|
327
335
|
var DEFAULT_GRAPH_WORKTREES_DIR = ".opencode/openteam-local/graph/worktrees";
|
|
328
336
|
var GraphConfigSchema = z2.object({
|
|
329
|
-
mode: GraphModeSchema.default("
|
|
337
|
+
mode: GraphModeSchema.default("active"),
|
|
330
338
|
engine: GraphEngineSchema.optional(),
|
|
331
339
|
killSwitch: z2.boolean().default(false),
|
|
332
340
|
journalRoot: z2.string().min(1).default(DEFAULT_GRAPH_JOURNAL_ROOT),
|
|
333
|
-
operatorApproval: z2.boolean().default(
|
|
341
|
+
operatorApproval: z2.boolean().default(true),
|
|
334
342
|
worktrees: z2.object({
|
|
335
343
|
enabled: z2.boolean().default(false)
|
|
336
344
|
}).strict().default({ enabled: false })
|
|
@@ -424,6 +432,9 @@ var OrchestratorRolesSchema = z2.record(z2.string().min(1), z2.unknown()).defaul
|
|
|
424
432
|
if (parsed.data.localModels !== undefined) {
|
|
425
433
|
profile.localModels = parsed.data.localModels;
|
|
426
434
|
}
|
|
435
|
+
if (parsed.data.localModelClass !== undefined) {
|
|
436
|
+
profile.localModelClass = parsed.data.localModelClass;
|
|
437
|
+
}
|
|
427
438
|
parsedRoles[roleID] = profile;
|
|
428
439
|
}
|
|
429
440
|
return parsedRoles;
|
|
@@ -450,7 +461,10 @@ var LocalRuntimeSchema = z2.object({
|
|
|
450
461
|
discovery: z2.enum(["cli", "sdk", "manual"]).optional(),
|
|
451
462
|
defaultModel: ModelRefSchema,
|
|
452
463
|
maxConcurrency: z2.number().int().positive().optional(),
|
|
453
|
-
|
|
464
|
+
modelConcurrency: z2.record(z2.string().min(1), z2.number().int().positive()).optional(),
|
|
465
|
+
reservedInteractiveSlots: z2.number().int().nonnegative().optional(),
|
|
466
|
+
thinkingModel: z2.string().min(1).optional(),
|
|
467
|
+
coderModel: z2.string().min(1).optional()
|
|
454
468
|
}).strict().superRefine((runtime, ctx) => {
|
|
455
469
|
const resolvedKind = runtime.kind ?? runtime.id;
|
|
456
470
|
if (!LocalRuntimeKindSchema.safeParse(resolvedKind).success) {
|
|
@@ -576,10 +590,10 @@ var OpenTeamConfigObjectSchema = z2.object({
|
|
|
576
590
|
storage: StorageConfigSchema,
|
|
577
591
|
memory: MemoryConfigSchema.optional(),
|
|
578
592
|
graph: GraphConfigSchema.default({
|
|
579
|
-
mode: "
|
|
593
|
+
mode: "active",
|
|
580
594
|
killSwitch: false,
|
|
581
595
|
journalRoot: DEFAULT_GRAPH_JOURNAL_ROOT,
|
|
582
|
-
operatorApproval:
|
|
596
|
+
operatorApproval: true,
|
|
583
597
|
worktrees: { enabled: false }
|
|
584
598
|
}),
|
|
585
599
|
orchestrator: OrchestratorConfigSchema.optional()
|
|
@@ -669,6 +683,16 @@ function classifyHeuristic(task) {
|
|
|
669
683
|
};
|
|
670
684
|
}
|
|
671
685
|
|
|
686
|
+
// src/local/slotPool.ts
|
|
687
|
+
var POOL_SEPARATOR = "::";
|
|
688
|
+
function slotPoolKey(runtime, modelID) {
|
|
689
|
+
if (modelID === undefined) {
|
|
690
|
+
return runtime.id;
|
|
691
|
+
}
|
|
692
|
+
const declared = runtime.modelConcurrency?.[modelID];
|
|
693
|
+
return declared === undefined ? runtime.id : `${runtime.id}${POOL_SEPARATOR}${modelID}`;
|
|
694
|
+
}
|
|
695
|
+
|
|
672
696
|
// src/capabilities/budget.ts
|
|
673
697
|
var WARNING_UTILIZATION = 0.8;
|
|
674
698
|
function projectedSpend(current, estimatedCostUSD) {
|
|
@@ -1604,9 +1628,19 @@ function configuredRuntime(runtimeId, config) {
|
|
|
1604
1628
|
function runtimeIdOfModel(model, config) {
|
|
1605
1629
|
return config.local.runtimes.find((runtime) => sameModel(runtime.defaultModel, model))?.id;
|
|
1606
1630
|
}
|
|
1607
|
-
function roleLocalModel(role, runtimeId, fallback) {
|
|
1631
|
+
function roleLocalModel(role, runtimeId, fallback, runtime) {
|
|
1608
1632
|
const pinned = role.localModels?.[runtimeId];
|
|
1609
|
-
|
|
1633
|
+
if (pinned !== undefined) {
|
|
1634
|
+
return { ...fallback, modelID: pinned };
|
|
1635
|
+
}
|
|
1636
|
+
const classModel = runtimeClassModel(role.localModelClass, runtime);
|
|
1637
|
+
return classModel === undefined ? fallback : { ...fallback, modelID: classModel };
|
|
1638
|
+
}
|
|
1639
|
+
function runtimeClassModel(modelClass, runtime) {
|
|
1640
|
+
if (modelClass === undefined || runtime === undefined) {
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
return modelClass === "thinking" ? runtime.thinkingModel : runtime.coderModel;
|
|
1610
1644
|
}
|
|
1611
1645
|
function selectDispatchRuntime(role, decision2, reachable, config) {
|
|
1612
1646
|
if (decision2.routeKind !== "local") {
|
|
@@ -1622,7 +1656,7 @@ function selectDispatchRuntime(role, decision2, reachable, config) {
|
|
|
1622
1656
|
if (runtime === undefined) {
|
|
1623
1657
|
continue;
|
|
1624
1658
|
}
|
|
1625
|
-
const model = roleLocalModel(role, runtimeId, runtime.defaultModel);
|
|
1659
|
+
const model = roleLocalModel(role, runtimeId, runtime.defaultModel, runtime);
|
|
1626
1660
|
return {
|
|
1627
1661
|
runtimeId,
|
|
1628
1662
|
decision: { ...decision2, selected: model }
|
|
@@ -1631,7 +1665,7 @@ function selectDispatchRuntime(role, decision2, reachable, config) {
|
|
|
1631
1665
|
}
|
|
1632
1666
|
const resolvedRuntimeId = runtimeIdOfModel(decision2.selected, config);
|
|
1633
1667
|
if (resolvedRuntimeId !== undefined) {
|
|
1634
|
-
const pinned = roleLocalModel(role, resolvedRuntimeId, decision2.selected);
|
|
1668
|
+
const pinned = roleLocalModel(role, resolvedRuntimeId, decision2.selected, configuredRuntime(resolvedRuntimeId, config));
|
|
1635
1669
|
if (pinned !== decision2.selected) {
|
|
1636
1670
|
return {
|
|
1637
1671
|
runtimeId: resolvedRuntimeId,
|
|
@@ -2267,18 +2301,19 @@ async function runRoleTaskWithCorrelation(input, deps, correlation, batch) {
|
|
|
2267
2301
|
if (runtimeId !== undefined && deps.runtimeLimiter !== undefined) {
|
|
2268
2302
|
const limiter = deps.runtimeLimiter;
|
|
2269
2303
|
const priority = priorityForRole(role);
|
|
2270
|
-
|
|
2304
|
+
const poolKey = slotPoolKey(configuredRuntime(runtimeId, input.config) ?? { id: runtimeId }, decision2.selected.modelID);
|
|
2305
|
+
release = limiter.tryAcquire(poolKey, priority);
|
|
2271
2306
|
let waitedMs = 0;
|
|
2272
2307
|
if (release === undefined) {
|
|
2273
|
-
const acquisition = await limiter.acquire(
|
|
2308
|
+
const acquisition = await limiter.acquire(poolKey, priority);
|
|
2274
2309
|
release = acquisition.release;
|
|
2275
2310
|
waitedMs = acquisition.waitedMs;
|
|
2276
2311
|
}
|
|
2277
2312
|
queueObservation = {
|
|
2278
2313
|
runtimeId,
|
|
2279
2314
|
waitedMs,
|
|
2280
|
-
activeCount: limiter.activeCount(
|
|
2281
|
-
queuedCount: limiter.queuedCount(
|
|
2315
|
+
activeCount: limiter.activeCount(poolKey),
|
|
2316
|
+
queuedCount: limiter.queuedCount(poolKey)
|
|
2282
2317
|
};
|
|
2283
2318
|
}
|
|
2284
2319
|
try {
|
|
@@ -3079,7 +3114,10 @@ var KNOWN_CONFIG_KEYS_BY_PATH = new Map([
|
|
|
3079
3114
|
"discovery",
|
|
3080
3115
|
"defaultModel",
|
|
3081
3116
|
"maxConcurrency",
|
|
3082
|
-
"
|
|
3117
|
+
"modelConcurrency",
|
|
3118
|
+
"reservedInteractiveSlots",
|
|
3119
|
+
"thinkingModel",
|
|
3120
|
+
"coderModel"
|
|
3083
3121
|
]
|
|
3084
3122
|
],
|
|
3085
3123
|
["local.runtimes[].defaultModel", MODEL_REF_KEYS],
|
|
@@ -9999,24 +10037,141 @@ function ensureFoundryV1BaseURL(endpoint) {
|
|
|
9999
10037
|
}
|
|
10000
10038
|
|
|
10001
10039
|
// src/local/lemonade.ts
|
|
10002
|
-
var LEMONADE_DEFAULT_BASE_URL = "http://localhost:
|
|
10040
|
+
var LEMONADE_DEFAULT_BASE_URL = "http://localhost:13305/api/v1";
|
|
10041
|
+
var NON_TEXT_LABELS = new Set([
|
|
10042
|
+
"image",
|
|
10043
|
+
"tts",
|
|
10044
|
+
"transcription",
|
|
10045
|
+
"embedding",
|
|
10046
|
+
"reranking"
|
|
10047
|
+
]);
|
|
10048
|
+
var PARALLEL_ARG = /(?:^|\s)(?:--parallel|-np)[\s=]+(\d+)(?!\d)/;
|
|
10003
10049
|
function createLemonadeAdapter() {
|
|
10050
|
+
const listModels = async (options) => {
|
|
10051
|
+
const baseURL = normalizeBaseURL(options.baseURL ?? LEMONADE_DEFAULT_BASE_URL);
|
|
10052
|
+
const downloaded = await listLemonadeModels(baseURL, options.fetch);
|
|
10053
|
+
const loaded = await readLoadedModels(baseURL, options.fetch);
|
|
10054
|
+
return enrichWithLoadState(downloaded, loaded);
|
|
10055
|
+
};
|
|
10004
10056
|
return {
|
|
10005
10057
|
id: "lemonade",
|
|
10006
10058
|
defaultBaseURL: LEMONADE_DEFAULT_BASE_URL,
|
|
10007
|
-
listModels
|
|
10008
|
-
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10059
|
+
listModels,
|
|
10060
|
+
async probe(options) {
|
|
10061
|
+
const baseURL = normalizeBaseURL(options.baseURL ?? LEMONADE_DEFAULT_BASE_URL);
|
|
10062
|
+
try {
|
|
10063
|
+
return {
|
|
10064
|
+
id: "lemonade",
|
|
10065
|
+
baseURL,
|
|
10066
|
+
reachable: true,
|
|
10067
|
+
models: await listModels({ ...options, baseURL }),
|
|
10068
|
+
probedAt: options.probedAt
|
|
10069
|
+
};
|
|
10070
|
+
} catch (error) {
|
|
10071
|
+
return unavailableSnapshot({
|
|
10072
|
+
id: "lemonade",
|
|
10073
|
+
baseURL,
|
|
10074
|
+
probedAt: options.probedAt,
|
|
10075
|
+
error
|
|
10076
|
+
});
|
|
10077
|
+
}
|
|
10017
10078
|
}
|
|
10018
10079
|
};
|
|
10019
10080
|
}
|
|
10081
|
+
async function listLemonadeModels(baseURL, fetch2) {
|
|
10082
|
+
const response = await fetch2(`${baseURL}/models`, {
|
|
10083
|
+
method: "GET",
|
|
10084
|
+
headers: { accept: "application/json" }
|
|
10085
|
+
});
|
|
10086
|
+
if (!response.ok) {
|
|
10087
|
+
throw new Error(`GET /models failed with HTTP ${response.status}`);
|
|
10088
|
+
}
|
|
10089
|
+
let payload;
|
|
10090
|
+
try {
|
|
10091
|
+
payload = await response.json();
|
|
10092
|
+
} catch (error) {
|
|
10093
|
+
throw new Error(`Malformed JSON from /models: ${errorMessage(error)}`);
|
|
10094
|
+
}
|
|
10095
|
+
const nonText = readNonTextModelIDs(payload);
|
|
10096
|
+
return parseOpenAIModels(payload).filter((model) => !nonText.has(model.modelID));
|
|
10097
|
+
}
|
|
10098
|
+
function readNonTextModelIDs(payload) {
|
|
10099
|
+
const ids = new Set;
|
|
10100
|
+
if (!isObject3(payload) || !Array.isArray(payload.data)) {
|
|
10101
|
+
return ids;
|
|
10102
|
+
}
|
|
10103
|
+
for (const entry of payload.data) {
|
|
10104
|
+
if (!isObject3(entry) || typeof entry.id !== "string") {
|
|
10105
|
+
continue;
|
|
10106
|
+
}
|
|
10107
|
+
if (isNonTextModel(entry)) {
|
|
10108
|
+
ids.add(entry.id);
|
|
10109
|
+
}
|
|
10110
|
+
}
|
|
10111
|
+
return ids;
|
|
10112
|
+
}
|
|
10113
|
+
function isNonTextModel(entry) {
|
|
10114
|
+
if (!Array.isArray(entry.labels)) {
|
|
10115
|
+
return false;
|
|
10116
|
+
}
|
|
10117
|
+
return entry.labels.some((label) => typeof label === "string" && NON_TEXT_LABELS.has(label));
|
|
10118
|
+
}
|
|
10119
|
+
async function readLoadedModels(baseURL, fetch2) {
|
|
10120
|
+
let payload;
|
|
10121
|
+
try {
|
|
10122
|
+
const response = await fetch2(`${baseURL}/health`, {
|
|
10123
|
+
method: "GET",
|
|
10124
|
+
headers: { accept: "application/json" }
|
|
10125
|
+
});
|
|
10126
|
+
if (!response.ok) {
|
|
10127
|
+
return;
|
|
10128
|
+
}
|
|
10129
|
+
payload = await response.json();
|
|
10130
|
+
} catch {
|
|
10131
|
+
return;
|
|
10132
|
+
}
|
|
10133
|
+
if (!isObject3(payload) || !Array.isArray(payload.all_models_loaded)) {
|
|
10134
|
+
return;
|
|
10135
|
+
}
|
|
10136
|
+
const loaded = new Map;
|
|
10137
|
+
for (const entry of payload.all_models_loaded) {
|
|
10138
|
+
if (!isObject3(entry) || typeof entry.model_name !== "string") {
|
|
10139
|
+
continue;
|
|
10140
|
+
}
|
|
10141
|
+
if (entry.loaded === false) {
|
|
10142
|
+
continue;
|
|
10143
|
+
}
|
|
10144
|
+
loaded.set(entry.model_name, { slots: readSlotCount(entry) });
|
|
10145
|
+
}
|
|
10146
|
+
return loaded;
|
|
10147
|
+
}
|
|
10148
|
+
function readSlotCount(entry) {
|
|
10149
|
+
const options = entry.recipe_options;
|
|
10150
|
+
if (!isObject3(options) || typeof options.llamacpp_args !== "string") {
|
|
10151
|
+
return;
|
|
10152
|
+
}
|
|
10153
|
+
const match = PARALLEL_ARG.exec(options.llamacpp_args);
|
|
10154
|
+
if (match === null) {
|
|
10155
|
+
return;
|
|
10156
|
+
}
|
|
10157
|
+
const parsed = Number.parseInt(match[1], 10);
|
|
10158
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
|
|
10159
|
+
}
|
|
10160
|
+
function enrichWithLoadState(models, loaded) {
|
|
10161
|
+
if (loaded === undefined) {
|
|
10162
|
+
return [...models];
|
|
10163
|
+
}
|
|
10164
|
+
return models.map((model) => {
|
|
10165
|
+
const hit = loaded.get(model.modelID);
|
|
10166
|
+
if (hit === undefined) {
|
|
10167
|
+
return { ...model, loaded: false };
|
|
10168
|
+
}
|
|
10169
|
+
return hit.slots === undefined ? { ...model, loaded: true } : { ...model, loaded: true, slots: hit.slots };
|
|
10170
|
+
});
|
|
10171
|
+
}
|
|
10172
|
+
function isObject3(value) {
|
|
10173
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10174
|
+
}
|
|
10020
10175
|
|
|
10021
10176
|
// src/local/lmstudio.ts
|
|
10022
10177
|
var LMSTUDIO_DEFAULT_BASE_URL = "http://localhost:1234/v1";
|
|
@@ -10245,6 +10400,9 @@ function createDetect(exec) {
|
|
|
10245
10400
|
reachable: snapshot.reachable,
|
|
10246
10401
|
models: snapshot.models.map((model) => model.modelID)
|
|
10247
10402
|
};
|
|
10403
|
+
if (snapshot.models.some((model) => model.loaded !== undefined)) {
|
|
10404
|
+
detected.modelInfo = snapshot.models;
|
|
10405
|
+
}
|
|
10248
10406
|
if (snapshot.error !== undefined) {
|
|
10249
10407
|
detected.error = snapshot.error;
|
|
10250
10408
|
}
|
|
@@ -10272,6 +10430,9 @@ function createProbeRuntime(exec) {
|
|
|
10272
10430
|
reachable: snapshot?.reachable ?? false,
|
|
10273
10431
|
models: snapshot?.models.map((model) => model.modelID) ?? []
|
|
10274
10432
|
};
|
|
10433
|
+
if (snapshot?.models.some((model) => model.loaded !== undefined) === true) {
|
|
10434
|
+
detected.modelInfo = snapshot.models;
|
|
10435
|
+
}
|
|
10275
10436
|
if (snapshot?.error !== undefined) {
|
|
10276
10437
|
detected.error = snapshot.error;
|
|
10277
10438
|
}
|
|
@@ -11001,6 +11162,22 @@ function renderConsoleStatus(console_) {
|
|
|
11001
11162
|
}
|
|
11002
11163
|
|
|
11003
11164
|
// src/commands/doctor.ts
|
|
11165
|
+
function runtimeClassLine(runtime) {
|
|
11166
|
+
if (runtime === undefined) {
|
|
11167
|
+
return;
|
|
11168
|
+
}
|
|
11169
|
+
const thinking = runtime.thinkingModel;
|
|
11170
|
+
const coder = runtime.coderModel;
|
|
11171
|
+
if (thinking === undefined && coder === undefined) {
|
|
11172
|
+
return;
|
|
11173
|
+
}
|
|
11174
|
+
const parts = [
|
|
11175
|
+
`thinking ${thinking ?? "(default)"}`,
|
|
11176
|
+
`coder ${coder ?? "(default)"}`,
|
|
11177
|
+
`default ${runtime.defaultModel.modelID}`
|
|
11178
|
+
];
|
|
11179
|
+
return ` ↳ ${parts.join(" · ")}`;
|
|
11180
|
+
}
|
|
11004
11181
|
function runtimeLine(snapshot, runtime) {
|
|
11005
11182
|
const mark = snapshot.reachable ? "✓" : "✗";
|
|
11006
11183
|
const detail = snapshot.reachable ? `${snapshot.models.length} model(s)` : snapshot.error ?? "unreachable";
|
|
@@ -11008,6 +11185,14 @@ function runtimeLine(snapshot, runtime) {
|
|
|
11008
11185
|
const slots = declared === undefined ? ` · ${DEFAULT_LOCAL_MAX_CONCURRENCY} slot(s) (default)` : ` · ${declared} slot(s) (declared)`;
|
|
11009
11186
|
return ` ${mark} ${snapshot.id.padEnd(14)} ${snapshot.baseURL || "(no baseURL)"} — ${detail}${slots}`;
|
|
11010
11187
|
}
|
|
11188
|
+
function runtimeModelSlotsLine(runtime) {
|
|
11189
|
+
const entries = Object.entries(runtime?.modelConcurrency ?? {});
|
|
11190
|
+
if (entries.length === 0) {
|
|
11191
|
+
return;
|
|
11192
|
+
}
|
|
11193
|
+
const detail = entries.map(([modelID, slots]) => `${modelID} ${slots} slot(s)`).join(" · ");
|
|
11194
|
+
return ` ↳ per-model slots: ${detail}`;
|
|
11195
|
+
}
|
|
11011
11196
|
function isNetworkRuntime(baseURL) {
|
|
11012
11197
|
if (baseURL.length === 0) {
|
|
11013
11198
|
return false;
|
|
@@ -11257,7 +11442,16 @@ function renderDoctor(input) {
|
|
|
11257
11442
|
lines.push(" (no runtime enabled)");
|
|
11258
11443
|
} else {
|
|
11259
11444
|
for (const snapshot of input.snapshots) {
|
|
11260
|
-
|
|
11445
|
+
const runtime = enabledRuntimes.find((r) => r.id === snapshot.id);
|
|
11446
|
+
lines.push(runtimeLine(snapshot, runtime));
|
|
11447
|
+
const classLine = runtimeClassLine(runtime);
|
|
11448
|
+
if (classLine !== undefined) {
|
|
11449
|
+
lines.push(classLine);
|
|
11450
|
+
}
|
|
11451
|
+
const modelSlots = runtimeModelSlotsLine(runtime);
|
|
11452
|
+
if (modelSlots !== undefined) {
|
|
11453
|
+
lines.push(modelSlots);
|
|
11454
|
+
}
|
|
11261
11455
|
}
|
|
11262
11456
|
}
|
|
11263
11457
|
for (const snapshot of input.snapshots) {
|
|
@@ -11993,6 +12187,15 @@ function buildOpenTeamConfig(answers) {
|
|
|
11993
12187
|
},
|
|
11994
12188
|
maxConcurrency: choice.maxConcurrency ?? DEFAULT_LOCAL_MAX_CONCURRENCY
|
|
11995
12189
|
};
|
|
12190
|
+
if (choice.modelConcurrency !== undefined && Object.keys(choice.modelConcurrency).length > 0) {
|
|
12191
|
+
runtime.modelConcurrency = { ...choice.modelConcurrency };
|
|
12192
|
+
}
|
|
12193
|
+
if (choice.thinkingModelID !== undefined && choice.thinkingModelID !== choice.defaultModelID) {
|
|
12194
|
+
runtime.thinkingModel = choice.thinkingModelID;
|
|
12195
|
+
}
|
|
12196
|
+
if (choice.coderModelID !== undefined && choice.coderModelID !== choice.defaultModelID) {
|
|
12197
|
+
runtime.coderModel = choice.coderModelID;
|
|
12198
|
+
}
|
|
11996
12199
|
if (choice.kind !== undefined && choice.kind !== choice.id) {
|
|
11997
12200
|
runtime.kind = choice.kind;
|
|
11998
12201
|
}
|
|
@@ -12023,7 +12226,7 @@ function buildOpenTeamConfig(answers) {
|
|
|
12023
12226
|
},
|
|
12024
12227
|
local: { runtimes },
|
|
12025
12228
|
privacyMode: answers.privacyMode,
|
|
12026
|
-
graph: { mode: "
|
|
12229
|
+
graph: { mode: "active" }
|
|
12027
12230
|
});
|
|
12028
12231
|
}
|
|
12029
12232
|
function buildOpencodeConfig(answers) {
|
|
@@ -12183,7 +12386,40 @@ async function chooseFrontier(prompt, profiles) {
|
|
|
12183
12386
|
}
|
|
12184
12387
|
return parseFrontierRef(selected);
|
|
12185
12388
|
}
|
|
12186
|
-
|
|
12389
|
+
function orderModelChoices(models, modelInfo) {
|
|
12390
|
+
const byID = new Map((modelInfo ?? []).map((model) => [model.modelID, model]));
|
|
12391
|
+
const anyLoadState = (modelInfo ?? []).some((model) => model.loaded !== undefined);
|
|
12392
|
+
const decorated = models.map((modelID, index) => {
|
|
12393
|
+
const info = byID.get(modelID);
|
|
12394
|
+
return {
|
|
12395
|
+
modelID,
|
|
12396
|
+
index,
|
|
12397
|
+
loaded: info?.loaded === true,
|
|
12398
|
+
slots: info?.slots
|
|
12399
|
+
};
|
|
12400
|
+
});
|
|
12401
|
+
if (!anyLoadState) {
|
|
12402
|
+
return decorated.map((entry) => ({
|
|
12403
|
+
value: entry.modelID,
|
|
12404
|
+
label: entry.modelID
|
|
12405
|
+
}));
|
|
12406
|
+
}
|
|
12407
|
+
return [...decorated].sort((a, b) => a.loaded === b.loaded ? a.index - b.index : a.loaded ? -1 : 1).map((entry) => ({
|
|
12408
|
+
value: entry.modelID,
|
|
12409
|
+
label: `${entry.modelID} ${modelStateSuffix(entry.loaded, entry.slots)}`
|
|
12410
|
+
}));
|
|
12411
|
+
}
|
|
12412
|
+
function modelStateSuffix(loaded, slots) {
|
|
12413
|
+
if (!loaded) {
|
|
12414
|
+
return "(not loaded)";
|
|
12415
|
+
}
|
|
12416
|
+
return slots === undefined ? "(loaded)" : `(loaded · ${slots} slot${slots === 1 ? "" : "s"})`;
|
|
12417
|
+
}
|
|
12418
|
+
function preferredModelID(models, modelInfo) {
|
|
12419
|
+
const loaded = (modelInfo ?? []).find((model) => model.loaded === true && models.includes(model.modelID));
|
|
12420
|
+
return loaded?.modelID ?? models[0];
|
|
12421
|
+
}
|
|
12422
|
+
async function chooseRuntimeModel(prompt, models, meta, modelInfo) {
|
|
12187
12423
|
if (models.length === 0) {
|
|
12188
12424
|
return prompt.text({
|
|
12189
12425
|
message: `Default model for ${meta.label} (no models detected)`,
|
|
@@ -12191,13 +12427,10 @@ async function chooseRuntimeModel(prompt, models, meta) {
|
|
|
12191
12427
|
initial: meta.fallbackModelID
|
|
12192
12428
|
});
|
|
12193
12429
|
}
|
|
12194
|
-
const first = models
|
|
12430
|
+
const first = preferredModelID(models, modelInfo);
|
|
12195
12431
|
const opts = {
|
|
12196
12432
|
message: `Default local model for ${meta.label}`,
|
|
12197
|
-
choices: models
|
|
12198
|
-
value: modelID,
|
|
12199
|
-
label: modelID
|
|
12200
|
-
}))
|
|
12433
|
+
choices: orderModelChoices(models, modelInfo)
|
|
12201
12434
|
};
|
|
12202
12435
|
if (first !== undefined) {
|
|
12203
12436
|
opts.initial = first;
|
|
@@ -12233,9 +12466,13 @@ function normalizeRemoteBaseURL(raw, defaultBaseURL) {
|
|
|
12233
12466
|
}
|
|
12234
12467
|
return `${url.origin}${url.pathname.replace(/\/+$/, "")}`;
|
|
12235
12468
|
}
|
|
12469
|
+
function connectionOf(info) {
|
|
12470
|
+
const models = info?.models ?? [];
|
|
12471
|
+
return info?.modelInfo === undefined ? { models } : { models, modelInfo: info.modelInfo };
|
|
12472
|
+
}
|
|
12236
12473
|
async function resolveRuntimeConnection(deps, meta, info) {
|
|
12237
12474
|
if (meta.defaultBaseURL === undefined) {
|
|
12238
|
-
return
|
|
12475
|
+
return connectionOf(info);
|
|
12239
12476
|
}
|
|
12240
12477
|
const location = await deps.prompt.select({
|
|
12241
12478
|
message: `Where does ${meta.label} run?`,
|
|
@@ -12254,40 +12491,88 @@ async function resolveRuntimeConnection(deps, meta, info) {
|
|
|
12254
12491
|
});
|
|
12255
12492
|
if (location === RUNTIME_LOCATION_LOCAL) {
|
|
12256
12493
|
const baseURL2 = info?.baseURL !== undefined && info.baseURL.length > 0 ? info.baseURL : meta.defaultBaseURL;
|
|
12257
|
-
return { baseURL: baseURL2,
|
|
12494
|
+
return { baseURL: baseURL2, ...connectionOf(info) };
|
|
12258
12495
|
}
|
|
12259
12496
|
const entered = await deps.prompt.text({
|
|
12260
12497
|
message: `${meta.label} URL on the network (OpenAI-compatible endpoint)`,
|
|
12261
12498
|
placeholder: remoteBaseURLExample(meta)
|
|
12262
12499
|
});
|
|
12263
12500
|
const baseURL = normalizeRemoteBaseURL(entered, meta.defaultBaseURL);
|
|
12264
|
-
let
|
|
12501
|
+
let probedRuntime;
|
|
12265
12502
|
if (deps.probeRuntime !== undefined) {
|
|
12266
12503
|
try {
|
|
12267
|
-
|
|
12268
|
-
models = probed.models;
|
|
12504
|
+
probedRuntime = await deps.probeRuntime(meta.id, baseURL);
|
|
12269
12505
|
} catch {}
|
|
12270
12506
|
}
|
|
12271
|
-
return { baseURL,
|
|
12507
|
+
return { baseURL, ...connectionOf(probedRuntime) };
|
|
12508
|
+
}
|
|
12509
|
+
async function chooseClassModel(prompt, models, meta, defaultModelID, modelClass, modelInfo) {
|
|
12510
|
+
const label = modelClass === "thinking" ? `Thinking model for ${meta.label} (design, analysis, review reasoning)` : `Coder model for ${meta.label} (writing and editing code)`;
|
|
12511
|
+
if (models.length === 0) {
|
|
12512
|
+
const raw = await prompt.text({
|
|
12513
|
+
message: `${label} — enter to reuse the default`,
|
|
12514
|
+
placeholder: defaultModelID,
|
|
12515
|
+
initial: defaultModelID
|
|
12516
|
+
});
|
|
12517
|
+
return raw.trim().length > 0 ? raw.trim() : defaultModelID;
|
|
12518
|
+
}
|
|
12519
|
+
return prompt.select({
|
|
12520
|
+
message: label,
|
|
12521
|
+
choices: orderModelChoices(models, modelInfo).map((choice) => ({
|
|
12522
|
+
value: choice.value,
|
|
12523
|
+
label: choice.value === defaultModelID ? `${choice.label} — default` : choice.label
|
|
12524
|
+
})),
|
|
12525
|
+
initial: defaultModelID
|
|
12526
|
+
});
|
|
12272
12527
|
}
|
|
12273
12528
|
async function resolveEnabledRuntime(deps, meta, info) {
|
|
12274
12529
|
const connection = await resolveRuntimeConnection(deps, meta, info);
|
|
12275
|
-
const defaultModelID = await chooseRuntimeModel(deps.prompt, connection.models, meta);
|
|
12530
|
+
const defaultModelID = await chooseRuntimeModel(deps.prompt, connection.models, meta, connection.modelInfo);
|
|
12531
|
+
const thinkingModelID = await chooseClassModel(deps.prompt, connection.models, meta, defaultModelID, "thinking", connection.modelInfo);
|
|
12532
|
+
const coderModelID = await chooseClassModel(deps.prompt, connection.models, meta, defaultModelID, "coder", connection.modelInfo);
|
|
12276
12533
|
const choice = {
|
|
12277
12534
|
id: meta.id,
|
|
12278
12535
|
enabled: true,
|
|
12279
12536
|
defaultModelID,
|
|
12537
|
+
thinkingModelID,
|
|
12538
|
+
coderModelID,
|
|
12280
12539
|
models: connection.models
|
|
12281
12540
|
};
|
|
12282
12541
|
if (connection.baseURL !== undefined) {
|
|
12283
12542
|
choice.baseURL = connection.baseURL;
|
|
12284
12543
|
}
|
|
12544
|
+
const modelConcurrency = reportedModelSlots(connection.modelInfo, [
|
|
12545
|
+
defaultModelID,
|
|
12546
|
+
thinkingModelID,
|
|
12547
|
+
coderModelID
|
|
12548
|
+
]);
|
|
12549
|
+
if (modelConcurrency !== undefined) {
|
|
12550
|
+
choice.modelConcurrency = modelConcurrency;
|
|
12551
|
+
}
|
|
12285
12552
|
return choice;
|
|
12286
12553
|
}
|
|
12554
|
+
function reportedModelSlots(modelInfo, usedModelIDs) {
|
|
12555
|
+
if (modelInfo === undefined) {
|
|
12556
|
+
return;
|
|
12557
|
+
}
|
|
12558
|
+
const used = new Set(usedModelIDs);
|
|
12559
|
+
const slots = {};
|
|
12560
|
+
for (const model of modelInfo) {
|
|
12561
|
+
if (model.slots !== undefined && used.has(model.modelID)) {
|
|
12562
|
+
slots[model.modelID] = model.slots;
|
|
12563
|
+
}
|
|
12564
|
+
}
|
|
12565
|
+
return Object.keys(slots).length > 0 ? slots : undefined;
|
|
12566
|
+
}
|
|
12287
12567
|
function instanceSuffix(index) {
|
|
12288
12568
|
return index < 26 ? String.fromCharCode(97 + index) : String(index + 1);
|
|
12289
12569
|
}
|
|
12290
|
-
async function promptSlotCount(prompt, meta) {
|
|
12570
|
+
async function promptSlotCount(prompt, meta, reported) {
|
|
12571
|
+
const detected = reportedSlotSummary(reported);
|
|
12572
|
+
if (detected !== undefined) {
|
|
12573
|
+
prompt.note(`${meta.label} reported its slots per model: ${detected.summary}. ` + "Slots belong to the loaded model, not the server, so openteam queues " + "each model against its own capacity instead of one shared number.", "Detected concurrency");
|
|
12574
|
+
return detected.max;
|
|
12575
|
+
}
|
|
12291
12576
|
const raw = await prompt.text({
|
|
12292
12577
|
message: `How many concurrent generation slots does ${meta.label} have? (match its real capacity; leaving one free keeps interactive work responsive)`,
|
|
12293
12578
|
placeholder: String(DEFAULT_LOCAL_MAX_CONCURRENCY),
|
|
@@ -12296,6 +12581,16 @@ async function promptSlotCount(prompt, meta) {
|
|
|
12296
12581
|
const parsed = Number.parseInt(raw.trim(), 10);
|
|
12297
12582
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_LOCAL_MAX_CONCURRENCY;
|
|
12298
12583
|
}
|
|
12584
|
+
function reportedSlotSummary(reported) {
|
|
12585
|
+
const entries = Object.entries(reported ?? {});
|
|
12586
|
+
if (entries.length === 0) {
|
|
12587
|
+
return;
|
|
12588
|
+
}
|
|
12589
|
+
return {
|
|
12590
|
+
summary: entries.map(([modelID, slots]) => `${modelID} = ${slots}`).join(", "),
|
|
12591
|
+
max: Math.max(...entries.map(([, slots]) => slots))
|
|
12592
|
+
};
|
|
12593
|
+
}
|
|
12299
12594
|
async function promptInstanceId(prompt, meta, defaultId, usedIds) {
|
|
12300
12595
|
for (;; ) {
|
|
12301
12596
|
const raw = await prompt.text({
|
|
@@ -12317,7 +12612,7 @@ async function resolveRuntimeInstances(deps, meta, info, usedIds) {
|
|
|
12317
12612
|
});
|
|
12318
12613
|
if (!fleet) {
|
|
12319
12614
|
const choice = await resolveEnabledRuntime(deps, meta, info);
|
|
12320
|
-
choice.maxConcurrency = await promptSlotCount(deps.prompt, meta);
|
|
12615
|
+
choice.maxConcurrency = await promptSlotCount(deps.prompt, meta, choice.modelConcurrency);
|
|
12321
12616
|
usedIds.add(choice.id);
|
|
12322
12617
|
return [choice];
|
|
12323
12618
|
}
|
|
@@ -12328,7 +12623,7 @@ async function resolveRuntimeInstances(deps, meta, info, usedIds) {
|
|
|
12328
12623
|
const choice = await resolveEnabledRuntime(deps, meta, info);
|
|
12329
12624
|
choice.id = id;
|
|
12330
12625
|
choice.kind = meta.id;
|
|
12331
|
-
choice.maxConcurrency = await promptSlotCount(deps.prompt, meta);
|
|
12626
|
+
choice.maxConcurrency = await promptSlotCount(deps.prompt, meta, choice.modelConcurrency);
|
|
12332
12627
|
usedIds.add(id);
|
|
12333
12628
|
instances.push(choice);
|
|
12334
12629
|
const another = await deps.prompt.confirm({
|
|
@@ -16164,7 +16459,7 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
|
|
|
16164
16459
|
// package.json
|
|
16165
16460
|
var package_default = {
|
|
16166
16461
|
name: "@jmanuelcorral/openteam",
|
|
16167
|
-
version: "0.
|
|
16462
|
+
version: "0.17.1",
|
|
16168
16463
|
packageManager: "bun@1.3.14",
|
|
16169
16464
|
description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
|
|
16170
16465
|
license: "MIT",
|
|
@@ -16320,7 +16615,7 @@ async function buildGateInput(config, storage, opencodeVersion, warn, packagedRe
|
|
|
16320
16615
|
} catch {}
|
|
16321
16616
|
const soak = await readAndParseCertificate(storage, SOAK_CERT_PATH, (raw) => parseSoakCertificate(raw, DEFAULT_SOAK_POLICY, soakLedgerDigest), (cert) => cert.digest);
|
|
16322
16617
|
const migration = "complete";
|
|
16323
|
-
const operatorApproval = config.graph?.operatorApproval ??
|
|
16618
|
+
const operatorApproval = config.graph?.operatorApproval ?? true;
|
|
16324
16619
|
return { shadow, release, soak, migration, operatorApproval };
|
|
16325
16620
|
}
|
|
16326
16621
|
var OPENCODE_VERSION_UNRESOLVED_REASON = "the opencode version is not determinable outside a running opencode session, " + "so the cutover gate cannot be verified here — the running plugin logs the " + "authoritative gate result and warns loudly on a real denial";
|