@jmanuelcorral/openteam 0.24.1 → 0.26.0
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/README.es.md +42 -10
- package/README.md +37 -8
- 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 +1200 -384
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/dispatch.d.ts.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/setup.d.ts +20 -15
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/config/schema.d.ts +101 -7
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/console/assets.d.ts.map +1 -1
- package/dist/console/conversation.d.ts +5 -1
- package/dist/console/conversation.d.ts.map +1 -1
- package/dist/console/credentials.d.ts +4 -0
- package/dist/console/credentials.d.ts.map +1 -0
- package/dist/console/inspector.d.ts +1 -1
- package/dist/console/inspector.d.ts.map +1 -1
- package/dist/console/refresh.d.ts +1 -1
- package/dist/console/refresh.d.ts.map +1 -1
- package/dist/console/render.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +657 -128
- package/dist/local/lemonadeResidency.d.ts +2 -14
- package/dist/local/lemonadeResidency.d.ts.map +1 -1
- package/dist/local/llamaSwap.d.ts +5 -0
- package/dist/local/llamaSwap.d.ts.map +1 -0
- package/dist/local/llamaSwapRequestGuard.d.ts +11 -0
- package/dist/local/llamaSwapRequestGuard.d.ts.map +1 -0
- package/dist/local/llamaSwapResidency.d.ts +9 -0
- package/dist/local/llamaSwapResidency.d.ts.map +1 -0
- package/dist/local/modelResidency.d.ts +1 -0
- package/dist/local/modelResidency.d.ts.map +1 -1
- package/dist/local/registry.d.ts.map +1 -1
- package/dist/local/residency.d.ts +16 -0
- package/dist/local/residency.d.ts.map +1 -0
- package/dist/local/types.d.ts +10 -2
- package/dist/local/types.d.ts.map +1 -1
- package/dist/messages/consoleDetail.d.ts +8 -5
- package/dist/messages/consoleDetail.d.ts.map +1 -1
- package/dist/messages/executionSetup.d.ts +22 -3
- package/dist/messages/executionSetup.d.ts.map +1 -1
- package/dist/messages/memoryRuntime.d.ts +6 -0
- package/dist/messages/memoryRuntime.d.ts.map +1 -0
- package/dist/messages/memoryTool.d.ts +1 -1
- package/dist/messages/memoryTool.d.ts.map +1 -1
- package/dist/messages/modelResidency.d.ts +20 -2
- package/dist/messages/modelResidency.d.ts.map +1 -1
- package/dist/plugin/availability.d.ts.map +1 -1
- package/dist/plugin/memoryTool.d.ts.map +1 -1
- package/dist/plugin/modelResidency.d.ts.map +1 -1
- package/dist/storage/index/memoryRuntime.d.ts.map +1 -1
- package/dist/web/conversationRoute.d.ts +2 -2
- package/dist/web/conversationRoute.d.ts.map +1 -1
- package/dist/web/server.d.ts +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -424,6 +424,9 @@ function migrateConfig(raw) {
|
|
|
424
424
|
var consoleDetailMessages = {
|
|
425
425
|
follow: "Follow new activity",
|
|
426
426
|
metadataOnly: "Lifecycle and telemetry stay metadata-only.",
|
|
427
|
+
logsDescription: "Current recorded conversations load automatically. Lifecycle and telemetry remain metadata-only; credentials are masked.",
|
|
428
|
+
credentialMasked: "[credential masked]",
|
|
429
|
+
offscreen: "Messages load automatically when this conversation is visible.",
|
|
427
430
|
filter: "Filter sessions",
|
|
428
431
|
incoming: "New activity available — show updates",
|
|
429
432
|
refreshError: "Live updates unavailable; retrying.",
|
|
@@ -444,7 +447,7 @@ var consoleDetailMessages = {
|
|
|
444
447
|
partialMetadata: "Some current session titles are unavailable or outside the bounded batch; fallback labels remain.",
|
|
445
448
|
rootCurrent: "Current whole orchestrator session; may contain multiple batches. Not a historical or batch-only transcript.",
|
|
446
449
|
recovered: "Read-only association verified from current opencode history; not an immutable lifecycle event.",
|
|
447
|
-
rootUnlinked: "Orchestrator session not recorded.
|
|
450
|
+
rootUnlinked: "Orchestrator session not recorded. A bounded, read-only relationship search is attempted when visible.",
|
|
448
451
|
rootUnavailable: "Orchestrator session unavailable: no unique, verifiable relationship was found in local history.",
|
|
449
452
|
rootAmbiguous: "Orchestrator session unavailable: conflicting or copied relationships are ambiguous.",
|
|
450
453
|
searchIncomplete: "Orchestrator association search incomplete or exceeded its bounded budget. No session was guessed.",
|
|
@@ -457,9 +460,9 @@ var consoleDetailMessages = {
|
|
|
457
460
|
arguments: "Arguments",
|
|
458
461
|
result: "Result",
|
|
459
462
|
loading: "Loading local conversation…",
|
|
460
|
-
current: "Present-day session, not a timeline reconstruction
|
|
461
|
-
historical: "Historical cursor: conversation content is hidden to avoid showing future mutable text. Return to live
|
|
462
|
-
disabled: "Content is disabled
|
|
463
|
+
current: "Present-day whole session, not a timeline reconstruction or per-attempt transcript. Visible messages update automatically while live.",
|
|
464
|
+
historical: "Historical cursor: conversation content is hidden to avoid showing future mutable text. Return to live to read current messages.",
|
|
465
|
+
disabled: "Content is disabled by console.sessionHistory.contentEnabled: false. Lifecycle and telemetry remain metadata-only.",
|
|
463
466
|
unavailable: "Conversation unavailable. Start an operator-owned local opencode serve against the same workspace and user history, configure console.sessionHistory.endpoint, then retry. No inference was requested.",
|
|
464
467
|
empty: "No readable user messages, assistant output or tool calls remain in this session.",
|
|
465
468
|
invalid: "This attempt has no recorded session in the selected run, or its history is incomplete.",
|
|
@@ -469,7 +472,7 @@ var consoleDetailMessages = {
|
|
|
469
472
|
invalidEndpoint: "Expected a loopback HTTP origin without credentials, path, query or fragment.",
|
|
470
473
|
readFailed: "Local session history could not be read.",
|
|
471
474
|
oversized: "Conversation exceeds the local size limit (4 MiB). No content was loaded.",
|
|
472
|
-
truncated: "Conversation display is truncated by local safety limits
|
|
475
|
+
truncated: "Conversation display is truncated by local safety limits. The newest bounded message window is shown; older messages or oversized fields may be omitted, not empty history.",
|
|
473
476
|
truncatedMarker: `
|
|
474
477
|
[truncated]`,
|
|
475
478
|
terminalGap: "Live collection was interrupted; events during the gap are unavailable. This is not complete session history.",
|
|
@@ -546,7 +549,7 @@ var ConsoleConfigSchema = z2.object({
|
|
|
546
549
|
openBrowser: z2.boolean().default(false),
|
|
547
550
|
remoteStorage: z2.boolean().default(false),
|
|
548
551
|
sessionHistory: z2.object({
|
|
549
|
-
contentEnabled: z2.boolean().default(
|
|
552
|
+
contentEnabled: z2.boolean().default(true),
|
|
550
553
|
endpoint: z2.string().refine(isLocalHistoryEndpoint, consoleDetailMessages.invalidEndpoint).optional()
|
|
551
554
|
}).strict().optional(),
|
|
552
555
|
terminal: z2.object({
|
|
@@ -684,7 +687,8 @@ var LocalRuntimeKindSchema = z2.enum([
|
|
|
684
687
|
"ollama",
|
|
685
688
|
"lmstudio",
|
|
686
689
|
"foundry-local",
|
|
687
|
-
"lemonade"
|
|
690
|
+
"lemonade",
|
|
691
|
+
"llama-swap"
|
|
688
692
|
]);
|
|
689
693
|
var LocalRuntimeSchema = z2.object({
|
|
690
694
|
id: z2.string().min(1),
|
|
@@ -704,13 +708,26 @@ var LocalRuntimeSchema = z2.object({
|
|
|
704
708
|
ctx.addIssue({
|
|
705
709
|
code: z2.ZodIssueCode.custom,
|
|
706
710
|
path: ["kind"],
|
|
707
|
-
message: `local runtime "${runtime.id}" has no resolvable kind: add "kind" ` + "(one of ollama, lmstudio, foundry-local, lemonade), since the id is
|
|
711
|
+
message: `local runtime "${runtime.id}" has no resolvable kind: add "kind" ` + "(one of ollama, lmstudio, foundry-local, lemonade, llama-swap), " + "since the id is not itself a runtime kind."
|
|
708
712
|
});
|
|
709
713
|
}
|
|
714
|
+
}).transform((runtime) => {
|
|
715
|
+
const resolvedKind = runtime.kind ?? runtime.id;
|
|
716
|
+
if (resolvedKind !== "llama-swap" || runtime.maxConcurrency !== undefined) {
|
|
717
|
+
return runtime;
|
|
718
|
+
}
|
|
719
|
+
return { ...runtime, maxConcurrency: 1 };
|
|
710
720
|
});
|
|
711
721
|
function localRuntimeKind(runtime) {
|
|
712
722
|
return runtime.kind ?? runtime.id;
|
|
713
723
|
}
|
|
724
|
+
function effectiveMaxConcurrency(runtime) {
|
|
725
|
+
if (runtime.maxConcurrency !== undefined) {
|
|
726
|
+
return runtime.maxConcurrency;
|
|
727
|
+
}
|
|
728
|
+
const kind = runtime.kind ?? (runtime.id !== undefined && LocalRuntimeKindSchema.safeParse(runtime.id).success ? runtime.id : undefined);
|
|
729
|
+
return kind === "llama-swap" ? 1 : DEFAULT_LOCAL_MAX_CONCURRENCY;
|
|
730
|
+
}
|
|
714
731
|
var MemoryScopeSchema = z2.enum(["project", "user"]);
|
|
715
732
|
var MemoryExtractionModeSchema = z2.enum([
|
|
716
733
|
"local",
|
|
@@ -891,6 +908,27 @@ import { isAbsolute, normalize, parse, resolve, sep } from "node:path";
|
|
|
891
908
|
import { createOpencodeClient } from "@opencode-ai/sdk";
|
|
892
909
|
import { z as z4 } from "zod";
|
|
893
910
|
|
|
911
|
+
// src/console/credentials.ts
|
|
912
|
+
var URL_WITH_AUTHORITY = /https?:\/\/[^\s/?#]+/giu;
|
|
913
|
+
function maskUrlCredentials(value) {
|
|
914
|
+
return value.replace(URL_WITH_AUTHORITY, (candidate) => {
|
|
915
|
+
const schemeEnd = candidate.indexOf("://") + 3;
|
|
916
|
+
const at = candidate.indexOf("@", schemeEnd);
|
|
917
|
+
if (at === -1)
|
|
918
|
+
return candidate;
|
|
919
|
+
const colon = candidate.indexOf(":", schemeEnd);
|
|
920
|
+
if (colon === -1 || colon <= schemeEnd || colon + 1 >= at || colon > at)
|
|
921
|
+
return candidate;
|
|
922
|
+
return `${candidate.slice(0, schemeEnd)}${consoleDetailMessages.credentialMasked}${candidate.slice(at)}`;
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
function maskConversationCredentials(value) {
|
|
926
|
+
return maskUrlCredentials(value.replace(/-----BEGIN (?:[A-Z]+ )?PRIVATE KEY-----[\s\S]*?(?:-----END (?:[A-Z]+ )?PRIVATE KEY-----|$)/gu, consoleDetailMessages.credentialMasked).replace(/\b(?:Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/giu, consoleDetailMessages.credentialMasked).replace(/\b(?:sk-[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{16,}|github_pat_[A-Za-z0-9_]{16,}|AKIA[A-Z0-9]{16}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)\b/gu, consoleDetailMessages.credentialMasked).replace(/(\b(?:password|passwd|secret|api[_-]?key|access[_-]?token|refresh[_-]?token|token|authorization|cookie|set-cookie)\b["']?\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;}\]]+)/giu, (_match, prefix) => prefix + consoleDetailMessages.credentialMasked));
|
|
927
|
+
}
|
|
928
|
+
function isCredentialKey(key) {
|
|
929
|
+
return /^(?:password|passwd|secret|api[_-]?key|access[_-]?token|refresh[_-]?token|token|authorization|cookie|set-cookie|private[_-]?key)$/iu.test(key);
|
|
930
|
+
}
|
|
931
|
+
|
|
894
932
|
// src/console/historyBoundary.ts
|
|
895
933
|
function only(keys) {
|
|
896
934
|
return (value) => {
|
|
@@ -1148,7 +1186,7 @@ class ConversationTooLargeError extends Error {
|
|
|
1148
1186
|
}
|
|
1149
1187
|
}
|
|
1150
1188
|
var Info = z4.preprocess(only(["id", "sessionID", "role", "time"]), z4.object({
|
|
1151
|
-
id: z4.string(),
|
|
1189
|
+
id: z4.string().min(1).max(256),
|
|
1152
1190
|
sessionID: z4.string(),
|
|
1153
1191
|
role: z4.enum(["user", "assistant"]),
|
|
1154
1192
|
time: z4.preprocess(only(["created", "completed"]), z4.object({
|
|
@@ -1168,7 +1206,7 @@ var Text = PartIdentity.extend({
|
|
|
1168
1206
|
});
|
|
1169
1207
|
var Tool = PartIdentity.extend({
|
|
1170
1208
|
type: z4.literal("tool"),
|
|
1171
|
-
callID: z4.string(),
|
|
1209
|
+
callID: z4.string().min(1).max(256),
|
|
1172
1210
|
tool: z4.string(),
|
|
1173
1211
|
state: z4.preprocess(only(["status", "input", "output", "error", "time"]), z4.object({
|
|
1174
1212
|
status: z4.enum(["pending", "running", "completed", "error"]),
|
|
@@ -1191,6 +1229,7 @@ function projectConversation(raw, sessionID) {
|
|
|
1191
1229
|
let remaining = limits.totalChars;
|
|
1192
1230
|
let toolsRemaining = limits.tools;
|
|
1193
1231
|
function text(value) {
|
|
1232
|
+
value = maskConversationCredentials(value);
|
|
1194
1233
|
const length = Math.min(value.length, limits.fieldChars, remaining);
|
|
1195
1234
|
remaining -= length;
|
|
1196
1235
|
if (length === value.length)
|
|
@@ -1222,7 +1261,7 @@ function projectConversation(raw, sessionID) {
|
|
|
1222
1261
|
truncated = true;
|
|
1223
1262
|
break;
|
|
1224
1263
|
}
|
|
1225
|
-
const child = bounded(value[key], depth + 1);
|
|
1264
|
+
const child = isCredentialKey(key) ? consoleDetailMessages.credentialMasked : bounded(value[key], depth + 1);
|
|
1226
1265
|
if (Array.isArray(value))
|
|
1227
1266
|
array.push(child);
|
|
1228
1267
|
else
|
|
@@ -1233,7 +1272,7 @@ function projectConversation(raw, sessionID) {
|
|
|
1233
1272
|
return text(JSON.stringify(bounded(input, 0), null, 2));
|
|
1234
1273
|
}
|
|
1235
1274
|
const messages = [];
|
|
1236
|
-
for (const rawRow of raw.slice(
|
|
1275
|
+
for (const rawRow of raw.slice(-limits.messages).reverse()) {
|
|
1237
1276
|
if (remaining <= 0) {
|
|
1238
1277
|
truncated = true;
|
|
1239
1278
|
break;
|
|
@@ -1242,12 +1281,13 @@ function projectConversation(raw, sessionID) {
|
|
|
1242
1281
|
if (row.info.sessionID !== sessionID)
|
|
1243
1282
|
throw new Error(consoleDetailMessages.readFailed);
|
|
1244
1283
|
const message = {
|
|
1245
|
-
id:
|
|
1284
|
+
id: row.info.id,
|
|
1246
1285
|
role: row.info.role,
|
|
1247
1286
|
createdAt: row.info.time.created,
|
|
1248
1287
|
...row.info.time.completed !== undefined ? { completedAt: row.info.time.completed } : {},
|
|
1249
1288
|
text: [],
|
|
1250
|
-
tools: []
|
|
1289
|
+
tools: [],
|
|
1290
|
+
order: []
|
|
1251
1291
|
};
|
|
1252
1292
|
if (row.parts.length > limits.parts)
|
|
1253
1293
|
truncated = true;
|
|
@@ -1271,16 +1311,19 @@ function projectConversation(raw, sessionID) {
|
|
|
1271
1311
|
if (part.sessionID !== sessionID || part.messageID !== row.info.id)
|
|
1272
1312
|
throw new Error(consoleDetailMessages.readFailed);
|
|
1273
1313
|
if (part.type === "text") {
|
|
1274
|
-
if (!part.ignored)
|
|
1314
|
+
if (!part.ignored) {
|
|
1315
|
+
message.order?.push({ type: "text", index: message.text.length });
|
|
1275
1316
|
message.text.push(text(part.text));
|
|
1317
|
+
}
|
|
1276
1318
|
} else {
|
|
1277
1319
|
if (toolsRemaining-- <= 0) {
|
|
1278
1320
|
truncated = true;
|
|
1279
1321
|
continue;
|
|
1280
1322
|
}
|
|
1281
1323
|
const state = part.state;
|
|
1324
|
+
message.order?.push({ type: "tool", index: message.tools.length });
|
|
1282
1325
|
message.tools.push({
|
|
1283
|
-
id:
|
|
1326
|
+
id: part.callID,
|
|
1284
1327
|
name: text(part.tool),
|
|
1285
1328
|
status: state.status,
|
|
1286
1329
|
arguments: argumentsText(state.input),
|
|
@@ -1385,7 +1428,7 @@ function createConversationReader(deps) {
|
|
|
1385
1428
|
if (workspaceKey(session.directory) !== workspaceKey(directory))
|
|
1386
1429
|
throw new Error(consoleDetailMessages.readFailed);
|
|
1387
1430
|
const time = z4.preprocess(only(["created"]), z4.object({ created: z4.number().finite().min(0).max(8640000000000000) }).strict()).safeParse(session.time);
|
|
1388
|
-
const title = boundedTitle(session.title);
|
|
1431
|
+
const title = boundedTitle(typeof session.title === "string" ? maskConversationCredentials(session.title) : session.title);
|
|
1389
1432
|
return {
|
|
1390
1433
|
id: session.id,
|
|
1391
1434
|
directory: session.directory,
|
|
@@ -1435,10 +1478,13 @@ function createConversationReader(deps) {
|
|
|
1435
1478
|
labels.delete(oldest);
|
|
1436
1479
|
return session;
|
|
1437
1480
|
},
|
|
1438
|
-
messages: async (id) => {
|
|
1481
|
+
messages: async (id, limit) => {
|
|
1439
1482
|
if (!verified.has(id))
|
|
1440
1483
|
throw new Error(consoleDetailMessages.readFailed);
|
|
1441
|
-
const result = await client.session.messages({
|
|
1484
|
+
const result = await client.session.messages({
|
|
1485
|
+
path: { id },
|
|
1486
|
+
...limit === undefined ? {} : { query: { limit } }
|
|
1487
|
+
});
|
|
1442
1488
|
if (!result.response.ok || result.error !== undefined)
|
|
1443
1489
|
throw new Error(consoleDetailMessages.readFailed);
|
|
1444
1490
|
return result.data;
|
|
@@ -1475,14 +1521,37 @@ function createConversationReader(deps) {
|
|
|
1475
1521
|
const read = async (endpoint, sessionID, signal) => withHistory(endpoint, signal, async (port) => {
|
|
1476
1522
|
const session = await port.get(sessionID);
|
|
1477
1523
|
return {
|
|
1478
|
-
...projectConversation(await port.messages(sessionID), sessionID),
|
|
1524
|
+
...projectConversation(await port.messages(sessionID, CONVERSATION_LIMITS.messages + 1), sessionID),
|
|
1479
1525
|
session: session.label
|
|
1480
1526
|
};
|
|
1481
1527
|
});
|
|
1528
|
+
const roots = new Map;
|
|
1482
1529
|
read.root = (endpoint, projection, signal) => withHistory(endpoint, signal, async (port) => {
|
|
1483
|
-
const
|
|
1530
|
+
const key = JSON.stringify([
|
|
1531
|
+
endpoint,
|
|
1532
|
+
projection.runID,
|
|
1533
|
+
projection.rootOperationID,
|
|
1534
|
+
projection.operations.flatMap((operation) => operation.attempts.map((attempt) => [
|
|
1535
|
+
operation.operationID,
|
|
1536
|
+
attempt.attemptID,
|
|
1537
|
+
attempt.sessionID
|
|
1538
|
+
]))
|
|
1539
|
+
]);
|
|
1540
|
+
const cached = roots.get(key);
|
|
1541
|
+
const recovered = cached ? {
|
|
1542
|
+
session: await port.get(cached),
|
|
1543
|
+
association: "sdk-parent"
|
|
1544
|
+
} : await recoverRootSession(port, projection);
|
|
1545
|
+
roots.delete(key);
|
|
1546
|
+
if (recovered.association === "sdk-parent")
|
|
1547
|
+
roots.set(key, recovered.session.id);
|
|
1548
|
+
if (roots.size > 32) {
|
|
1549
|
+
const oldest = roots.keys().next().value;
|
|
1550
|
+
if (oldest !== undefined)
|
|
1551
|
+
roots.delete(oldest);
|
|
1552
|
+
}
|
|
1484
1553
|
return {
|
|
1485
|
-
...projectConversation(await port.messages(recovered.session.id), recovered.session.id),
|
|
1554
|
+
...projectConversation(await port.messages(recovered.session.id, CONVERSATION_LIMITS.messages + 1), recovered.session.id),
|
|
1486
1555
|
session: recovered.session.label,
|
|
1487
1556
|
association: recovered.association
|
|
1488
1557
|
};
|
|
@@ -3356,6 +3425,23 @@ var executionSetupMessages = {
|
|
|
3356
3425
|
`),
|
|
3357
3426
|
localOnlyTitle: "Local execution",
|
|
3358
3427
|
localRequiredError: "local execution requires an enabled local runtime",
|
|
3428
|
+
llamaSwap: {
|
|
3429
|
+
label: "llama-swap",
|
|
3430
|
+
noLoadedModels: "llama-swap has no canonical models verified as ready with no active routing profile. Keep the intended model ready through the operator's existing runtime controls, then rerun setup; manual IDs cannot bypass admission.",
|
|
3431
|
+
modelNotLoaded: (model) => `llama-swap model "${model}" is not positively verified as canonical and ready without an active routing profile. No substitute was selected.`,
|
|
3432
|
+
probeRequired: "llama-swap setup requires a fresh read-only residency probe before writing configuration. Manual model IDs cannot bypass this requirement.",
|
|
3433
|
+
probeFailed: "llama-swap readiness could not be verified. Check the configured /v1/models catalog and origin /running and /api/profiles endpoints; authenticated runtimes are not supported.",
|
|
3434
|
+
endpointMismatch: "llama-swap endpoint/provider mapping differs from the selected runtime. Reconcile the configured origin or /v1 URL and provider; no configuration was written.",
|
|
3435
|
+
modelIdentityUnknown: (model) => `llama-swap model "${model}" has no usable registered physical identity. Fix its model declaration; setup will not guess an alias.`,
|
|
3436
|
+
declaredLimits: "llama-swap context and output limits are operator-declared metadata, not measured per-slot capacities. Missing output metadata uses openteam's client policy default, not a guessed model cap. Unknown context remains 0 and disables automatic compaction. Tool support remains unknown without evidence.",
|
|
3437
|
+
declaredLimitsTitle: "Declared llama-swap limits",
|
|
3438
|
+
outputReduction: (model, previous, ceiling) => `${model}: reducing limit.output from ${previous} to ${ceiling} to respect the declared runtime ceiling. Smaller operator limits are preserved.`,
|
|
3439
|
+
outputReductionTitle: "Declared output ceiling",
|
|
3440
|
+
outputChanged: (model, ceiling) => `${model}: fresh runtime metadata declares an output ceiling of ${ceiling} below the prepared configuration. Rerun setup to review the updated limit; no configuration was written.`
|
|
3441
|
+
},
|
|
3442
|
+
residencyV2Title: "Resident runtime transport compatibility",
|
|
3443
|
+
residencyV2Restriction: (providers) => `Project policies disable opencode's separate native/core V2 transport for resident-only providers: ${providers.join(", ")}. Guarded V1 provider transport remains supported; this does not ban the public SDK v2 client library. Higher-precedence policies can override project restrictions. Readiness preflight is not an atomic no-autoload guarantee.`,
|
|
3444
|
+
retainedResidencyProviderUnprotected: (providerID) => `Cannot safely retain resident-only provider "${providerID}": the effective configuration must retain its matching runtime, disabled when deselected. Reconcile the runtime/provider configuration; no configuration was written.`,
|
|
3359
3445
|
lemonadeDetectedHint: (loaded, advertised) => `detected - ${loaded} confirmed loaded of ${advertised} advertised text model(s)`,
|
|
3360
3446
|
lemonadeNoLoadedModels: "Lemonade has no text models verified as loaded and ready. Keep the intended model active in Lemonade and rerun setup; inactive or unknown models cannot be selected or entered manually.",
|
|
3361
3447
|
lemonadeModelNotLoaded: (model) => `Lemonade model "${model}" is not positively verified as loaded and ready. Keep that exact text model active and rerun setup; no substitute was selected and no implicit loading is allowed.`,
|
|
@@ -3370,11 +3456,11 @@ var executionSetupMessages = {
|
|
|
3370
3456
|
lemonadeV2InvalidPolicies: "Cannot safely preserve experimental.policies: experimental must be an object and policies must be an array of provider.use statements with a string resource and allow/deny effect. Fix the project configuration before rerunning setup.",
|
|
3371
3457
|
lemonadeV2Title: "Lemonade transport compatibility",
|
|
3372
3458
|
lemonadeV2Restriction: (providers) => `Project policies disable native V2 Lemonade access for: ${providers.join(", ")}. Ordinary guarded V1 configuration remains supported; V2 is not an openteam-hook execution path. Higher-precedence or global policies can override this project restriction; it is not a server-atomic no-autoload guarantee.`,
|
|
3373
|
-
lemonadeInventory: (instanceID, providerID, models) => {
|
|
3459
|
+
lemonadeInventory: (instanceID, providerID, models, runtime = "Lemonade") => {
|
|
3374
3460
|
const inventory = models.map((model) => `${model.modelID} (${model.loaded === true ? "loaded" : model.loaded === false ? "inactive" : "residency unknown"})`).join(", ");
|
|
3375
|
-
return
|
|
3461
|
+
return `${runtime} inventory ${instanceID} (provider ${providerID}): ${inventory || "no advertised text models"}. Only confirmed loaded text models count as ready.`;
|
|
3376
3462
|
},
|
|
3377
|
-
lemonadeAgentResidency: (agent, model, state) =>
|
|
3463
|
+
lemonadeAgentResidency: (agent, model, state, runtime = "Lemonade") => `${runtime} readiness: ${agent} (${model}): ${state === "not-loaded" ? "inactive (not ready)" : "residency unknown"}; advertised inventory is not loaded readiness. Keep this exact model active and verify residency before use; requests without verified readiness are blocked.`,
|
|
3378
3464
|
mixedPrimaryQuestion: "Primary coordinator model",
|
|
3379
3465
|
mixedPrimaryLocal: (model) => `Use local thinking model ${model}`,
|
|
3380
3466
|
mixedPrimaryFrontier: "Choose a frontier model",
|
|
@@ -3478,6 +3564,8 @@ var executionPolicyMessages = {
|
|
|
3478
3564
|
primaryLocalMismatchRemedy: (sourceFile) => ` remedy: re-run \`openteam setup\` to regenerate ${sourceFile} and opencode.json from the same primary policy, then re-run \`openteam doctor\`.`
|
|
3479
3565
|
};
|
|
3480
3566
|
var doctorMessages = {
|
|
3567
|
+
defaultRuntimeSlots: (slots) => ` · ${slots} slot(s) (default)`,
|
|
3568
|
+
llamaSwapConcurrency: " llama-swap does not report per-model slots; openteam uses the configured runtime-wide concurrency limit. No per-model capacity was inferred.",
|
|
3481
3569
|
localModelLimits: {
|
|
3482
3570
|
section: " local model limits:",
|
|
3483
3571
|
healthy: " ✓ enabled local provider models declare valid output and context limits in opencode.json.",
|
|
@@ -3494,7 +3582,7 @@ var doctorMessages = {
|
|
|
3494
3582
|
invalidInput: "invalid limit.input (expected a positive integer token count)",
|
|
3495
3583
|
outputExceedsContext: "limit.output is greater than or equal to limit.context, so the configured context leaves no usable input window",
|
|
3496
3584
|
outputExceedsInput: "limit.input is less than or equal to the reserved output budget, so compaction would have no usable input threshold",
|
|
3497
|
-
outputRemedy: " remedy: re-run `openteam setup` to write openteam's 8 192
|
|
3585
|
+
outputRemedy: " remedy: re-run `openteam setup` to write openteam's local output policy (at most 8 192 tokens, narrowed by declared runtime ceilings and smaller context/input budgets), then re-run `openteam doctor`.",
|
|
3498
3586
|
unconfiguredLimitRemedy: " remedy: the optional limit block may remain absent; to configure explicit limits, manually add a complete limit object in opencode.json with positive output and non-negative context (0 means unknown), because setup fills an absent limit block only for models the runtime currently discovers. Then re-run `openteam doctor`.",
|
|
3499
3587
|
detectedContextRemedy: " remedy: re-run `openteam setup` while the runtime is reachable to copy the detected usable context budget, then re-run `openteam doctor`.",
|
|
3500
3588
|
unknownContextRemedy: " remedy: re-run `openteam setup` to write the required context:0 sentinel; automatic compaction remains disabled until a positive context is configured.",
|
|
@@ -4482,234 +4570,286 @@ async function handleLifecycleHistoryRoute(request, response, url, deps) {
|
|
|
4482
4570
|
// src/console/inspector.ts
|
|
4483
4571
|
var CONVERSATION_CLIENT_JS = `
|
|
4484
4572
|
function wireConversationInspector(){
|
|
4573
|
+
if(document.__conversationsWired){return}document.__conversationsWired=true;
|
|
4574
|
+
var msg=${JSON.stringify(consoleDetailMessages)},limits=${JSON.stringify(CONVERSATION_LIMITS)};
|
|
4575
|
+
var records=new Map(),recoveries=new Map(),stopped=false,timer=null,scheduled=null,active=0,serial=0,retained=null;
|
|
4485
4576
|
var mount=document.getElementById('lifecycle-timeline-mount');
|
|
4486
|
-
if(
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
function
|
|
4496
|
-
if(
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
if(
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
retained=null;
|
|
4577
|
+
function el(tag,text){var node=document.createElement(tag);if(text!==undefined){node.textContent=text}return node}
|
|
4578
|
+
function setText(node,text){if(node.textContent!==text){node.textContent=text}}
|
|
4579
|
+
function visible(node){
|
|
4580
|
+
if(document.hidden||!node.isConnected){return false}
|
|
4581
|
+
var parent=node;
|
|
4582
|
+
while(parent){if(parent.hidden){return false}parent=parent.parentElement}
|
|
4583
|
+
var rect=node.getBoundingClientRect();
|
|
4584
|
+
return rect.bottom>=0&&rect.top<(window.innerHeight||1000);
|
|
4585
|
+
}
|
|
4586
|
+
function cancel(record){
|
|
4587
|
+
if(record.request){record.request.abort();record.request=null}
|
|
4588
|
+
if(record.finish){record.finish();record.finish=null}
|
|
4589
|
+
record.version++;
|
|
4590
|
+
}
|
|
4591
|
+
function discard(record){
|
|
4592
|
+
cancel(record);record.panel.remove();record.body.textContent='';record.source.textContent='';record.rows.clear();records.delete(record.key);
|
|
4593
|
+
}
|
|
4594
|
+
function clear(scope){
|
|
4595
|
+
Array.from(records.values()).forEach(function(record){
|
|
4596
|
+
if(scope==='native'&&record.selection.target==='session'){return}
|
|
4597
|
+
discard(record);
|
|
4598
|
+
});
|
|
4599
|
+
if(scope!=='native'){retained=null}
|
|
4600
|
+
}
|
|
4601
|
+
function recovery(record){
|
|
4602
|
+
var saved=recoveries.get(record.key);
|
|
4603
|
+
if(saved&&saved.until<=Date.now()){recoveries.delete(record.key);return null}
|
|
4604
|
+
return saved||null;
|
|
4605
|
+
}
|
|
4606
|
+
function rootRecovery(record,data){
|
|
4607
|
+
return record.selection.target==='root'&&!record.selection.session&&data.kind==='unavailable'&&
|
|
4608
|
+
[msg.rootUnavailable,msg.rootAmbiguous,msg.searchIncomplete].includes(data.message)?
|
|
4609
|
+
{message:data.message,until:Date.now()+30000}:null;
|
|
4610
|
+
}
|
|
4611
|
+
function rememberRecovery(record,saved){
|
|
4612
|
+
if(!saved){recoveries.delete(record.key);return}
|
|
4613
|
+
recoveries.delete(record.key);recoveries.set(record.key,saved);
|
|
4614
|
+
if(recoveries.size>128){recoveries.delete(recoveries.keys().next().value)}
|
|
4615
|
+
}
|
|
4616
|
+
function desired(){
|
|
4617
|
+
var result=[];
|
|
4618
|
+
if(stopped||document.hidden){return result}
|
|
4619
|
+
var explicitHistorical=!!((mount&&mount.getAttribute('data-conversation-cursor'))||new URLSearchParams(window.location.search).has('cursor'));
|
|
4620
|
+
var transitioning=mount&&mount.getAttribute('data-conversation-transition')==='1';
|
|
4621
|
+
if(mount){
|
|
4622
|
+
var notice=mount.querySelector('[data-conversation-historical]');
|
|
4623
|
+
if(explicitHistorical){
|
|
4624
|
+
if(!notice){notice=el('p',msg.historical);notice.setAttribute('data-conversation-historical','');mount.appendChild(notice)}
|
|
4625
|
+
}else if(notice){notice.remove()}
|
|
4626
|
+
}
|
|
4627
|
+
if(mount&&!explicitHistorical&&!transitioning){
|
|
4628
|
+
mount.querySelectorAll('[data-inspect-root],[data-inspect-attempt]').forEach(function(button){
|
|
4629
|
+
if(button.disabled){return}
|
|
4630
|
+
var root=button.hasAttribute('data-inspect-root');
|
|
4631
|
+
var session=button.closest(root?'[data-root-session-id]':'[data-lifecycle-session-id]');
|
|
4632
|
+
var host=root?button.closest('.lct-operation-card'):button.closest('[data-lifecycle-attempt-id]');
|
|
4633
|
+
if(!session||!host){return}
|
|
4634
|
+
var selection={target:root?'root':'attempt',run:mount.getAttribute('data-lifecycle-run'),
|
|
4635
|
+
operation:button.getAttribute('data-inspect-operation'),attempt:root?null:button.getAttribute('data-inspect-attempt'),
|
|
4636
|
+
session:session.getAttribute(root?'data-root-session-id':'data-lifecycle-session-id')};
|
|
4637
|
+
if(!selection.run||!selection.operation){return}
|
|
4638
|
+
result.push({key:JSON.stringify(selection),selection:selection,host:host,button:button});
|
|
4639
|
+
});
|
|
4550
4640
|
}
|
|
4641
|
+
document.querySelectorAll('[data-session-conversation]').forEach(function(host){
|
|
4642
|
+
var selection={target:'session',session:host.getAttribute('data-session-conversation')};
|
|
4643
|
+
if(explicitHistorical){
|
|
4644
|
+
if(visible(host)){setText(host,msg.historical)}
|
|
4645
|
+
return;
|
|
4646
|
+
}
|
|
4647
|
+
result.push({key:JSON.stringify(selection),selection:selection,host:host});
|
|
4648
|
+
});
|
|
4649
|
+
return result;
|
|
4551
4650
|
}
|
|
4552
|
-
function
|
|
4553
|
-
panel.
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
var
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
var
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
var
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4651
|
+
function create(item){
|
|
4652
|
+
var panel=el('section');panel.className='lc-conversation';panel.id='lc-conversation-'+(++serial);
|
|
4653
|
+
panel.setAttribute('data-conversation-inspector','');
|
|
4654
|
+
panel.setAttribute('data-conversation-target',item.selection.target);
|
|
4655
|
+
panel.setAttribute('data-conversation-session',item.selection.session||'');
|
|
4656
|
+
if(item.selection.run){panel.setAttribute('data-conversation-run',item.selection.run)}
|
|
4657
|
+
if(item.selection.attempt){panel.setAttribute('data-conversation-attempt',item.selection.attempt)}
|
|
4658
|
+
var heading=el('h3',msg.title);panel.appendChild(heading);
|
|
4659
|
+
var status=el('p',msg.loading);status.setAttribute('role','status');panel.appendChild(status);
|
|
4660
|
+
var source=el('p');panel.appendChild(source);
|
|
4661
|
+
var body=el('div');body.setAttribute('data-conversation-messages','');panel.appendChild(body);
|
|
4662
|
+
var refresh=el('button',msg.refresh);refresh.type='button';refresh.setAttribute('data-conversation-refresh','');panel.appendChild(refresh);
|
|
4663
|
+
var record={key:item.key,selection:item.selection,host:item.host,panel:panel,status:status,source:source,body:body,rows:new Map(),
|
|
4664
|
+
request:null,finish:null,version:0,due:0,button:item.button};
|
|
4665
|
+
refresh.addEventListener('click',function(){record.due=0;recoveries.delete(record.key);sync()});
|
|
4666
|
+
item.host.appendChild(panel);records.set(item.key,record);
|
|
4667
|
+
return record;
|
|
4668
|
+
}
|
|
4669
|
+
function matches(record,data){
|
|
4670
|
+
var s=record.selection;
|
|
4671
|
+
return data.sessionID&&(s.session?data.sessionID===s.session:['sdk-parent','tool-result'].includes(data.association))&&
|
|
4672
|
+
(s.target==='session'?data.target==='session'&&!data.runID:
|
|
4673
|
+
data.runID===s.run&&data.operationID===s.operation&&data.target===s.target&&
|
|
4674
|
+
(s.target==='root'?data.attemptID===undefined:data.attemptID===s.attempt));
|
|
4675
|
+
}
|
|
4676
|
+
function render(record,messages){
|
|
4677
|
+
var remaining=limits.totalChars,tools=0,truncated=false,used=new Set();
|
|
4678
|
+
function text(value){
|
|
4679
|
+
value=String(value===undefined?'':value);
|
|
4680
|
+
var length=Math.min(value.length,limits.fieldChars+msg.truncatedMarker.length,remaining);
|
|
4681
|
+
remaining-=length;if(length<value.length){truncated=true}return value.slice(0,length);
|
|
4682
|
+
}
|
|
4683
|
+
if(messages.length>limits.messages){truncated=true}
|
|
4684
|
+
messages.slice(0,limits.messages).forEach(function(message){
|
|
4685
|
+
if(!message||!['user','assistant'].includes(message.role)||typeof message.id!=='string'||used.has(message.id)){return}
|
|
4686
|
+
used.add(message.id);
|
|
4687
|
+
var row=record.rows.get(message.id);
|
|
4688
|
+
if(!row){
|
|
4689
|
+
var article=el('article');article.setAttribute('data-conversation-role',message.role);
|
|
4690
|
+
article.appendChild(el('h4',message.role==='user'?msg.prompt:msg.assistant));
|
|
4691
|
+
var time=el('time');article.appendChild(time);
|
|
4692
|
+
row={node:article,time:time,texts:[],tools:new Map()};record.rows.set(message.id,row);record.body.appendChild(article);
|
|
4693
|
+
}
|
|
4694
|
+
var selection=window.getSelection&&window.getSelection();
|
|
4695
|
+
if(selection&&!selection.isCollapsed&&row.node.contains(selection.anchorNode)){return}
|
|
4696
|
+
setText(row.time,consoleLabels.utcTime(message.createdAt));
|
|
4697
|
+
var parts=(message.text||[]).slice(0,limits.parts);
|
|
4698
|
+
parts.forEach(function(value,index){
|
|
4699
|
+
if(!row.texts[index]){row.texts[index]=el('pre');row.node.insertBefore(row.texts[index],row.node.querySelector('details'))}
|
|
4700
|
+
setText(row.texts[index],text(value));
|
|
4701
|
+
});
|
|
4702
|
+
while(row.texts.length>parts.length){row.texts.pop().remove()}
|
|
4703
|
+
var seenTools=new Set();
|
|
4704
|
+
(message.tools||[]).slice(0,limits.parts).forEach(function(tool){
|
|
4705
|
+
if(tools++>=limits.tools||remaining<=0){truncated=true;return}
|
|
4706
|
+
seenTools.add(tool.id);
|
|
4707
|
+
var call=row.tools.get(tool.id);
|
|
4708
|
+
if(!call){
|
|
4709
|
+
var detail=el('details');detail.setAttribute('data-tool-call',tool.id);detail.open=true;
|
|
4710
|
+
var summary=el('summary');detail.appendChild(summary);
|
|
4711
|
+
detail.appendChild(el('h5',msg.arguments));var args=el('pre');detail.appendChild(args);
|
|
4712
|
+
detail.appendChild(el('h5',msg.result));var result=el('pre');detail.appendChild(result);
|
|
4713
|
+
call={node:detail,summary:summary,args:args,result:result};row.tools.set(tool.id,call);row.node.appendChild(detail);
|
|
4606
4714
|
}
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
if(bringIntoView){panel.querySelector('h3').scrollIntoView({block:'start',inline:'nearest'})}
|
|
4610
|
-
}).catch(function(){
|
|
4611
|
-
if(epoch===current){status.textContent=msg.unavailable;panel.setAttribute('data-conversation-kind','unavailable')}
|
|
4612
|
-
}).finally(function(){
|
|
4613
|
-
clearTimeout(timeout);
|
|
4614
|
-
if(epoch!==current){return}
|
|
4615
|
-
request=null;
|
|
4616
|
-
panel.setAttribute('aria-busy','false');
|
|
4617
|
-
var refresh=element('button',msg.refresh);refresh.type='button';refresh.setAttribute('data-conversation-refresh','');
|
|
4618
|
-
refresh.addEventListener('click',load);panel.appendChild(refresh);
|
|
4715
|
+
setText(call.summary,text(tool.name)+' · '+text(tool.status));
|
|
4716
|
+
setText(call.args,text(tool.arguments));setText(call.result,text(tool.result===undefined?msg.pending:tool.result));
|
|
4619
4717
|
});
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
var calls=element('div');calls.setAttribute('data-conversation-tool-list','');tools.appendChild(calls);
|
|
4628
|
-
panel.appendChild(tools);
|
|
4629
|
-
function text(value){
|
|
4630
|
-
value=String(value);
|
|
4631
|
-
var size=Math.min(value.length,limits.fieldChars+msg.truncatedMarker.length,remaining);
|
|
4632
|
-
remaining-=size;
|
|
4633
|
-
if(size<value.length){truncated=true}
|
|
4634
|
-
return value.slice(0,size);
|
|
4635
|
-
}
|
|
4636
|
-
['user','assistant'].forEach(function(role){
|
|
4637
|
-
var section=element('section');section.setAttribute('data-conversation-role',role);
|
|
4638
|
-
section.appendChild(element('h4',role==='user'?msg.prompt:msg.assistant));
|
|
4639
|
-
var hasText=false;
|
|
4640
|
-
messages.filter(function(m){return m.role===role}).forEach(function(m){
|
|
4641
|
-
var item=element('article');item.appendChild(element('time',consoleLabels.utcTime(m.createdAt)));
|
|
4642
|
-
if((m.text||[]).length>limits.parts){truncated=true}
|
|
4643
|
-
(m.text||[]).slice(0,limits.parts).forEach(function(value){
|
|
4644
|
-
if(!value){return}
|
|
4645
|
-
hasText=true;
|
|
4646
|
-
if(remaining>0){item.appendChild(element('pre',text(value)))}else{truncated=true}
|
|
4718
|
+
row.tools.forEach(function(call,id){if(!seenTools.has(id)){call.node.remove();row.tools.delete(id)}});
|
|
4719
|
+
if(Array.isArray(message.order)){
|
|
4720
|
+
var previous=row.time;
|
|
4721
|
+
message.order.slice(0,limits.parts).forEach(function(part){
|
|
4722
|
+
var node=part.type==='text'?row.texts[part.index]:row.tools.get((message.tools[part.index]||{}).id);
|
|
4723
|
+
node=node&&node.node||node;
|
|
4724
|
+
if(node){if(previous.nextSibling!==node){row.node.insertBefore(node,previous.nextSibling||null)}previous=node}
|
|
4647
4725
|
});
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4726
|
+
}
|
|
4727
|
+
});
|
|
4728
|
+
record.rows.forEach(function(row,id){if(!used.has(id)){row.node.remove();record.rows.delete(id)}});
|
|
4729
|
+
if(truncated){setText(record.status,msg.current+'\\n'+msg.truncated)}
|
|
4730
|
+
}
|
|
4731
|
+
function load(record){
|
|
4732
|
+
var selected=record.selection,controller=new AbortController(),version=++record.version;
|
|
4733
|
+
record.request=controller;active++;record.panel.setAttribute('aria-busy','true');
|
|
4734
|
+
var finished=false;
|
|
4735
|
+
record.finish=function(){if(!finished){finished=true;active--;clearTimeout(timeout)}};
|
|
4736
|
+
var query=new URLSearchParams({token:document.documentElement.getAttribute('data-token')||''});
|
|
4737
|
+
var path='/api/graph/conversation';
|
|
4738
|
+
if(selected.target==='session'){path='/api/console/conversation';query.set('session',selected.session)}
|
|
4739
|
+
else{query.set('run',selected.run);query.set('operation',selected.operation);
|
|
4740
|
+
if(selected.target==='root'){query.set('target','root')}else{query.set('attempt',selected.attempt)}}
|
|
4741
|
+
var timeout=setTimeout(function(){
|
|
4742
|
+
if(record.version!==version){return}
|
|
4743
|
+
cancel(record);record.due=Date.now()+30000;setText(record.status,msg.unavailable);
|
|
4744
|
+
record.panel.setAttribute('aria-busy','false');schedule();
|
|
4745
|
+
},15000);
|
|
4746
|
+
fetch(path+'?'+query,{signal:controller.signal,cache:'no-store'}).then(function(response){return response.json()}).then(function(data){
|
|
4747
|
+
if(record.version!==version||!records.has(record.key)||!visible(record.host)||controller.signal.aborted){return}
|
|
4748
|
+
if((data.kind==='conversation'||data.kind==='empty')&&!matches(record,data)){data={kind:'unavailable',message:msg.unavailable}}
|
|
4749
|
+
var negative=rootRecovery(record,data);
|
|
4750
|
+
record.panel.setAttribute('data-conversation-kind',data.kind||'unavailable');
|
|
4751
|
+
setText(record.status,data.message||((data.kind==='conversation'||data.kind==='empty')?msg.current:msg.unavailable));
|
|
4752
|
+
if(data.kind==='conversation'||data.kind==='empty'){
|
|
4753
|
+
recoveries.delete(record.key);
|
|
4754
|
+
record.panel.setAttribute('data-conversation-session',data.sessionID);
|
|
4755
|
+
setText(record.source,(data.session&&data.session.title||msg.session)+' · '+data.sessionID);
|
|
4756
|
+
var focus=record.panel.contains(document.activeElement)?document.activeElement:null;
|
|
4757
|
+
render(record,data.messages||[]);
|
|
4758
|
+
if(focus&&focus.isConnected&&focus!==document.activeElement&&
|
|
4759
|
+
(!document.activeElement||document.activeElement===document.body)){focus.focus({preventScroll:true})}
|
|
4760
|
+
if(data.session&&data.session.id===data.sessionID){
|
|
4761
|
+
document.dispatchEvent(new CustomEvent('console-session-metadata',{detail:{session:data.session,runID:data.runID,target:selected.target}}));
|
|
4762
|
+
}
|
|
4763
|
+
}else{rememberRecovery(record,negative);record.body.textContent='';record.source.textContent='';record.rows.clear()}
|
|
4764
|
+
record.due=['disabled','unauthorized','historical','invalid','oversized'].includes(data.kind)?Infinity:Date.now()+(data.kind==='unavailable'?30000:2000);
|
|
4765
|
+
}).catch(function(){
|
|
4766
|
+
if(record.version===version&&!controller.signal.aborted){
|
|
4767
|
+
recoveries.delete(record.key);
|
|
4768
|
+
setText(record.status,msg.unavailable);record.panel.setAttribute('data-conversation-kind','unavailable');
|
|
4769
|
+
record.body.textContent='';record.source.textContent='';record.rows.clear();record.due=Date.now()+30000;
|
|
4770
|
+
}
|
|
4771
|
+
}).finally(function(){
|
|
4772
|
+
if(!finished){finished=true;clearTimeout(timeout);active--}
|
|
4773
|
+
if(record.version===version){record.request=null;record.panel.setAttribute('aria-busy','false');if(controller.signal.aborted){record.due=Date.now()+30000;setText(record.status,msg.unavailable)}}
|
|
4774
|
+
schedule();
|
|
4775
|
+
});
|
|
4776
|
+
}
|
|
4777
|
+
function sync(){
|
|
4778
|
+
var items=desired(),byKey=new Map(items.map(function(item){return [item.key,item]}));
|
|
4779
|
+
Array.from(records.values()).forEach(function(record){
|
|
4780
|
+
var item=byKey.get(record.key);
|
|
4781
|
+
if(!item||!visible(item.host)){
|
|
4782
|
+
if(item&&item.host.isConnected){item.host.style.minHeight=item.host.getBoundingClientRect().height+'px'}
|
|
4783
|
+
discard(record);return;
|
|
4784
|
+
}
|
|
4785
|
+
record.host=item.host;
|
|
4786
|
+
if(record.panel.parentElement!==item.host){item.host.appendChild(record.panel)}
|
|
4787
|
+
});
|
|
4788
|
+
items.forEach(function(item){
|
|
4789
|
+
if(!visible(item.host)){return}
|
|
4790
|
+
var record=records.get(item.key);
|
|
4791
|
+
if(!record&&records.size<8){
|
|
4792
|
+
if(item.selection.target==='session'){item.host.textContent=''}
|
|
4793
|
+
record=create(item);
|
|
4794
|
+
}
|
|
4795
|
+
if(!record){return}
|
|
4796
|
+
if(item.button){item.button.hidden=true;item.button.setAttribute('aria-controls',record.panel.id)}
|
|
4797
|
+
var saved=recovery(record);
|
|
4798
|
+
if(saved&&!record.rows.size&&!record.request){record.panel.setAttribute('data-conversation-kind','unavailable');setText(record.status,saved.message)}
|
|
4799
|
+
if(!record.request&&active<2&&record.due<=Date.now()&&!saved){load(record)}
|
|
4651
4800
|
});
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4801
|
+
}
|
|
4802
|
+
function schedule(){
|
|
4803
|
+
if(stopped||scheduled!==null){return}
|
|
4804
|
+
scheduled=setTimeout(function(){scheduled=null;sync()},50);
|
|
4805
|
+
}
|
|
4806
|
+
document.addEventListener('conversation-selection',function(event){
|
|
4807
|
+
clear(event&&event.detail&&event.detail.scope==='native'?'native':'all');
|
|
4808
|
+
sync();
|
|
4809
|
+
});
|
|
4810
|
+
document.addEventListener('conversation-tree-replacing',function(){
|
|
4811
|
+
var scroll=document.querySelector('.scroll'),tree=mount&&mount.querySelector('[data-lifecycle-tree-mount]');
|
|
4812
|
+
retained={focus:document.activeElement,scroll:scroll,scrollTop:scroll&&scroll.scrollTop,treeTop:tree&&tree.scrollTop,positions:[]};
|
|
4813
|
+
records.forEach(function(record){
|
|
4814
|
+
[record.body].concat(Array.from(record.panel.querySelectorAll('pre'))).forEach(function(node){
|
|
4815
|
+
retained.positions.push([node,node.scrollTop,node.scrollLeft]);
|
|
4662
4816
|
});
|
|
4663
4817
|
});
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
target:root?'root':'attempt',
|
|
4680
|
-
session:session.getAttribute(root?'data-root-session-id':'data-lifecycle-session-id'),
|
|
4681
|
-
cursor:mount.getAttribute('data-conversation-cursor')||null
|
|
4682
|
-
};
|
|
4683
|
-
if(!selectedButton()){close();return}
|
|
4684
|
-
panel.setAttribute('data-conversation-run',selection.run);
|
|
4685
|
-
panel.setAttribute('data-conversation-operation',selection.operation);
|
|
4686
|
-
if(selection.attempt){panel.setAttribute('data-conversation-attempt',selection.attempt)}
|
|
4687
|
-
panel.setAttribute('data-conversation-target',selection.target);
|
|
4688
|
-
panel.setAttribute('data-conversation-session',selection.session);
|
|
4689
|
-
reanchor();
|
|
4690
|
-
panel.hidden=false;
|
|
4691
|
-
if(selection.cursor!==null){reset();panel.setAttribute('data-conversation-kind','historical');panel.appendChild(element('p',msg.historical));showHeading()}
|
|
4692
|
-
else{load(true)}
|
|
4818
|
+
records.forEach(function(record){if(record.selection.target!=='session'){record.panel.remove()}});
|
|
4819
|
+
});
|
|
4820
|
+
document.addEventListener('conversation-tree-committed',function(){
|
|
4821
|
+
sync();
|
|
4822
|
+
if(retained){
|
|
4823
|
+
retained.positions.forEach(function(item){if(item[0].isConnected){item[0].scrollTop=item[1];item[0].scrollLeft=item[2]}});
|
|
4824
|
+
var tree=mount&&mount.querySelector('[data-lifecycle-tree-mount]');
|
|
4825
|
+
if(tree&&retained.treeTop!==null){tree.scrollTop=retained.treeTop}
|
|
4826
|
+
if(retained.scroll){retained.scroll.scrollTop=retained.scrollTop}
|
|
4827
|
+
if(retained.focus&&retained.focus.isConnected&&retained.focus!==document.activeElement&&
|
|
4828
|
+
(!document.activeElement||document.activeElement===document.body||document.activeElement===document.documentElement)){
|
|
4829
|
+
retained.focus.focus({preventScroll:true});
|
|
4830
|
+
}
|
|
4831
|
+
retained=null;
|
|
4832
|
+
}
|
|
4693
4833
|
});
|
|
4694
|
-
document.addEventListener('conversation-selection',
|
|
4695
|
-
document.addEventListener('
|
|
4696
|
-
document.addEventListener('
|
|
4697
|
-
window.addEventListener('
|
|
4834
|
+
document.addEventListener('console-conversation-selection',sync);
|
|
4835
|
+
document.addEventListener('visibilitychange',function(){if(document.hidden){clear()}else{sync()}});
|
|
4836
|
+
document.addEventListener('scroll',schedule,true);
|
|
4837
|
+
window.addEventListener('resize',schedule);
|
|
4838
|
+
window.addEventListener('pagehide',function(){stopped=true;clear();clearInterval(timer);clearTimeout(scheduled);scheduled=null});
|
|
4839
|
+
window.addEventListener('pageshow',function(event){if(event.persisted){stopped=false;timer=setInterval(sync,2000);sync()}});
|
|
4840
|
+
timer=setInterval(sync,2000);sync();
|
|
4698
4841
|
}
|
|
4699
4842
|
`;
|
|
4700
4843
|
var CONVERSATION_STYLE = `
|
|
4701
|
-
.lc-conversation{margin-top:12px;padding:12px;min-width:0;border:1px solid var(--cp-border);border-radius:
|
|
4844
|
+
.lc-conversation{margin-top:12px;padding:12px;min-width:0;border:1px solid var(--cp-border,var(--border));border-radius:12px;background:var(--cp-surface,var(--surface));color:var(--cp-text,var(--fg));overflow-anchor:none}
|
|
4702
4845
|
.lc-conversation[hidden]{display:none}
|
|
4703
|
-
.lc-conversation-head{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px}
|
|
4704
4846
|
.lc-conversation h3,.lc-conversation h4,.lc-conversation p{margin:8px 0}
|
|
4705
|
-
.lc-conversation
|
|
4706
|
-
.lc-conversation pre{white-space:pre-wrap;overflow-wrap:anywhere;max-height:400px;overflow:auto;background:var(--cp-surface-soft);padding:12px;font-family:
|
|
4707
|
-
.lc-conversation
|
|
4708
|
-
.lc-conversation
|
|
4709
|
-
.lc-conversation
|
|
4710
|
-
.lc-conversation
|
|
4711
|
-
.lc-conversation time{font-size:11px;color:var(--cp-text-muted)}
|
|
4712
|
-
.lc-conversation button,[data-inspect-attempt],[data-inspect-root]{padding:6px 10px;margin:6px;border:1px solid var(--cp-border);border-radius:.625rem;background:var(--cp-surface);color:var(--cp-accent)}
|
|
4847
|
+
.lc-conversation [data-conversation-messages]{max-height:520px;overflow:auto;overflow-anchor:none}
|
|
4848
|
+
.lc-conversation pre{white-space:pre-wrap;overflow-wrap:anywhere;max-height:400px;overflow:auto;background:var(--cp-surface-soft,var(--bg));padding:12px;font-family:var(--font-mono)}
|
|
4849
|
+
.lc-conversation details{margin:12px 0;padding:8px;border:1px solid var(--cp-border,var(--border));border-radius:8px}
|
|
4850
|
+
.lc-conversation summary{cursor:pointer;color:var(--cp-accent,var(--accent))}
|
|
4851
|
+
.lc-conversation time{font-size:11px;color:var(--cp-text-muted,var(--muted))}
|
|
4852
|
+
.lc-conversation button,[data-inspect-attempt],[data-inspect-root]{padding:6px 10px;margin:6px;border:1px solid var(--cp-border,var(--border));border-radius:8px;background:var(--cp-surface,var(--surface));color:var(--cp-accent,var(--accent))}
|
|
4713
4853
|
.lct-operation-name{display:block;font-size:16px}
|
|
4714
4854
|
.lct-operation-head details,.lct-session details{font-size:11px;color:var(--cp-text-muted)}
|
|
4715
4855
|
.lct-session small,.session-btn small{display:block;color:var(--cp-text-muted)}
|
|
@@ -4743,7 +4883,7 @@ var CONSOLE_REFRESH_JS = `
|
|
|
4743
4883
|
if(old.getAttribute('data-label-fallback')!==next.textContent){old.setAttribute('data-label-fallback',next.textContent)}
|
|
4744
4884
|
return;
|
|
4745
4885
|
}
|
|
4746
|
-
if(old.matches('input,textarea,select,.term,.pty,.graph-drill,[data-config-mount]')){return}
|
|
4886
|
+
if(old.matches('input,textarea,select,.term,.pty,.graph-drill,[data-config-mount],[data-session-conversation]')){return}
|
|
4747
4887
|
Array.from(old.attributes).forEach(function(a){
|
|
4748
4888
|
if(a.name!=='open'&&a.name!=='hidden'&&a.name!=='aria-selected'&&!next.hasAttribute(a.name)){old.removeAttribute(a.name)}
|
|
4749
4889
|
});
|
|
@@ -4957,6 +5097,8 @@ svg.graph{display:block}
|
|
|
4957
5097
|
.graph-drill .drilllog .tl.error{color:#f85149}
|
|
4958
5098
|
`;
|
|
4959
5099
|
var CONSOLE_CLIENT_JS = `
|
|
5100
|
+
${LABELS_CLIENT_JS}
|
|
5101
|
+
${CONVERSATION_CLIENT_JS}
|
|
4960
5102
|
(function(){
|
|
4961
5103
|
var detailMsg=${JSON.stringify(consoleDetailMessages)};
|
|
4962
5104
|
var terminalCollectors=[];
|
|
@@ -5011,6 +5153,7 @@ var CONSOLE_CLIENT_JS = `
|
|
|
5011
5153
|
if(show){matched=true;if(!screen.hidden){connectTerminal(cards[j],id)}}
|
|
5012
5154
|
}
|
|
5013
5155
|
updateTerminalLeases();
|
|
5156
|
+
document.dispatchEvent(new CustomEvent('console-conversation-selection'));
|
|
5014
5157
|
return matched;
|
|
5015
5158
|
}
|
|
5016
5159
|
if(!screen.__logsWired){
|
|
@@ -5071,6 +5214,7 @@ var CONSOLE_CLIENT_JS = `
|
|
|
5071
5214
|
if(name==='config'&&activeScreen){loadConfig(activeScreen)}
|
|
5072
5215
|
if(name==='logs'&&activeScreen){wireLogsScreen(activeScreen)}
|
|
5073
5216
|
updateTerminalLeases();
|
|
5217
|
+
document.dispatchEvent(new CustomEvent('console-conversation-selection'));
|
|
5074
5218
|
try{localStorage.setItem('openteam.screen',name)}catch(e){}
|
|
5075
5219
|
document.body.classList.remove('nav-open');
|
|
5076
5220
|
return true;
|
|
@@ -5472,8 +5616,6 @@ var LIFECYCLE_THEME_BOOT = `
|
|
|
5472
5616
|
})();
|
|
5473
5617
|
`;
|
|
5474
5618
|
var LIFECYCLE_CLIENT_JS = `
|
|
5475
|
-
${LABELS_CLIENT_JS}
|
|
5476
|
-
${CONVERSATION_CLIENT_JS}
|
|
5477
5619
|
function wireLifecycleTimeline(){
|
|
5478
5620
|
var mount=document.getElementById('lifecycle-timeline-mount');
|
|
5479
5621
|
if(!mount){return}
|
|
@@ -5520,7 +5662,7 @@ function wireLifecycleTimeline(){
|
|
|
5520
5662
|
treeMountEl.setAttribute('inert','');
|
|
5521
5663
|
treeMountEl.querySelectorAll('[data-inspect-attempt],[data-inspect-root]').forEach(function(button){button.disabled=true});
|
|
5522
5664
|
}
|
|
5523
|
-
document.dispatchEvent(new CustomEvent('conversation-selection'));
|
|
5665
|
+
document.dispatchEvent(new CustomEvent('conversation-selection',{detail:{scope:'native'}}));
|
|
5524
5666
|
}
|
|
5525
5667
|
function commitConversation(run,cursor){
|
|
5526
5668
|
cursor=cursor===null?null:String(cursor);
|
|
@@ -5982,6 +6124,7 @@ function wireLifecycleTimeline(){
|
|
|
5982
6124
|
inspect.setAttribute('aria-expanded','false');
|
|
5983
6125
|
inspect.setAttribute('aria-controls','lc-attempt-conversation');
|
|
5984
6126
|
inspect.setAttribute('data-inspect-attempt',attempt.attemptID);
|
|
6127
|
+
inspect.hidden=true;
|
|
5985
6128
|
inspect.setAttribute('data-inspect-operation',attempt.operationID);
|
|
5986
6129
|
session.appendChild(inspect);
|
|
5987
6130
|
if(attempt.parentSessionID){
|
|
@@ -6134,7 +6277,7 @@ function wireLifecycleTimeline(){
|
|
|
6134
6277
|
var time=makeElement('small','',${JSON.stringify(consoleDetailMessages.timeUnknown)});time.setAttribute('data-session-time',rootID);rootSession.appendChild(time);
|
|
6135
6278
|
var id=makeElement('code','',rootID);rootSession.appendChild(id);
|
|
6136
6279
|
}else{rootSession.appendChild(makeElement('span','',${JSON.stringify(consoleDetailMessages.rootUnlinked)}))}
|
|
6137
|
-
var inspect=makeElement('button','',message('inspect'));inspect.type='button';inspect.setAttribute('data-inspect-root','');inspect.setAttribute('data-inspect-operation',op.operationID);
|
|
6280
|
+
var inspect=makeElement('button','',message('inspect'));inspect.type='button';inspect.hidden=true;inspect.setAttribute('data-inspect-root','');inspect.setAttribute('data-inspect-operation',op.operationID);rootSession.appendChild(inspect);
|
|
6138
6281
|
card.insertBefore(rootSession,card.querySelector('.lct-attempts'));
|
|
6139
6282
|
}
|
|
6140
6283
|
var children=childrenByParent.get(op.operationID)||[];
|
|
@@ -6746,6 +6889,7 @@ function sessionDetailCard(tab, terminalEnabled, ptyEnabled, visible) {
|
|
|
6746
6889
|
const idSuffix = domIdPart(tab.sessionID);
|
|
6747
6890
|
const body = [
|
|
6748
6891
|
sessionHeaderPanel(tab),
|
|
6892
|
+
`<div data-session-conversation="${escapeHtml(tab.sessionID)}"></div>`,
|
|
6749
6893
|
terminalEnabled ? terminalPanel(tab) : "",
|
|
6750
6894
|
terminalEnabled && ptyEnabled ? ptyPanel(tab) : "",
|
|
6751
6895
|
routesPanel(tab.recentRoutes, idSuffix),
|
|
@@ -7131,7 +7275,7 @@ function lcSessionHtml(attempt, role) {
|
|
|
7131
7275
|
sessionNameHtml(attempt.sessionID, role),
|
|
7132
7276
|
`<small data-session-time="${escapeHtml(attempt.sessionID)}">${escapeHtml(consoleDetailMessages.timeUnknown)}</small>`,
|
|
7133
7277
|
`<details><summary>${escapeHtml(consoleDetailMessages.technicalID)}</summary><code>${escapeHtml(attempt.sessionID)}</code></details>`,
|
|
7134
|
-
`<button type="button"
|
|
7278
|
+
`<button type="button" hidden data-inspect-operation="${escapeHtml(attempt.operationID)}" data-inspect-attempt="${escapeHtml(attempt.attemptID)}">${escapeHtml(consoleDetailMessages.inspectConversation)}</button>`,
|
|
7135
7279
|
attempt.parentSessionID === undefined ? "" : `<span class="lct-parent-session">${escapeHtml(lifecycleMessages.parentSessionLabel)} <code>${escapeHtml(attempt.parentSessionID)}</code></span>`,
|
|
7136
7280
|
"</div>"
|
|
7137
7281
|
].join("");
|
|
@@ -7218,7 +7362,7 @@ function lcOperationHtml(operation, operationIDs, childrenHtml, depth, rootSessi
|
|
|
7218
7362
|
isRoot && operation.kind === "coordinator" ? [
|
|
7219
7363
|
`<div class="lct-session" data-root-session-id="${escapeHtml(rootSessionID ?? "")}">`,
|
|
7220
7364
|
rootSessionID ? `${sessionNameHtml(rootSessionID, consoleDetailMessages.orchestrator)} <small data-session-time="${escapeHtml(rootSessionID)}">${escapeHtml(consoleDetailMessages.timeUnknown)}</small><details><summary>${escapeHtml(consoleDetailMessages.technicalID)}</summary><code>${escapeHtml(rootSessionID)}</code></details>` : `<span>${escapeHtml(consoleDetailMessages.rootUnlinked)}</span>`,
|
|
7221
|
-
`<button type="button" data-inspect-root data-inspect-operation="${escapeHtml(operation.operationID)}"
|
|
7365
|
+
`<button type="button" hidden data-inspect-root data-inspect-operation="${escapeHtml(operation.operationID)}">${escapeHtml(consoleDetailMessages.inspectConversation)}</button>`,
|
|
7222
7366
|
"</div>"
|
|
7223
7367
|
].join("") : "",
|
|
7224
7368
|
`<section class="lct-attempts" aria-label="${escapeHtml(lifecycleMessages.attemptsLabel)}">${attempts}</section>`,
|
|
@@ -7597,11 +7741,11 @@ function renderConsoleHtml(state, options = {}) {
|
|
|
7597
7741
|
const body = [
|
|
7598
7742
|
screenSection("tree", treeCopy.label, treeCopy.heading, treeCopy.description, treeBody, true),
|
|
7599
7743
|
screenSection("routing", "Routing and telemetry", "Routing + telemetry", "Aggregate routing and cost telemetry from <code>GET /api/state</code> across all sessions.", routingScreenBody(state)),
|
|
7600
|
-
screenSection("logs", "Logs and session activity", "Logs / activity",
|
|
7744
|
+
screenSection("logs", "Logs and session activity", "Logs / activity", consoleDetailMessages.logsDescription, `<div data-log-controls><label>${escapeHtml(consoleDetailMessages.filter)} <input type="search" data-log-filter></label> <label><input type="checkbox" data-log-follow> ${escapeHtml(consoleDetailMessages.follow)}</label> <button type="button" data-log-incoming hidden>${escapeHtml(consoleDetailMessages.incoming)}</button></div><div data-log-content>${logsScreenBody(state, terminalEnabled, ptyEnabled)}</div>`),
|
|
7601
7745
|
screenSection("config", "Editable configuration", 'Configuration <span class="pill warn">editable</span>', "Edit the whole openteam JSON configuration. Roles are editable inside the JSON when present; unsupported surfaces are not shown.", configScreenBody(options.configEditor?.hasToken === true))
|
|
7602
7746
|
].join("");
|
|
7603
7747
|
const session = state.session.id === undefined ? "" : ` · session ${escapeHtml(state.session.id)}`;
|
|
7604
|
-
const tokenAttr =
|
|
7748
|
+
const tokenAttr = options.consoleToken !== undefined ? ` data-token="${escapeHtml(options.consoleToken)}"${terminalEnabled ? ' data-terminal="1"' : ""}${terminalEnabled && ptyEnabled ? ' data-pty="1"' : ""}${graphDrilldown ? ' data-drilldown="1"' : ""}${configEnabled ? ' data-config="1"' : ""}` : "";
|
|
7605
7749
|
return [
|
|
7606
7750
|
"<!doctype html>",
|
|
7607
7751
|
`<html lang="en" data-generated="${escapeHtml(state.generatedAt)}" data-refresh="${refreshMs}"${tokenAttr}>`,
|
|
@@ -7610,7 +7754,7 @@ function renderConsoleHtml(state, options = {}) {
|
|
|
7610
7754
|
'<meta name="viewport" content="width=device-width,initial-scale=1">',
|
|
7611
7755
|
"<title>openteam console</title>",
|
|
7612
7756
|
lifecycleEnabled ? `<script>${LIFECYCLE_THEME_BOOT}</script>` : "",
|
|
7613
|
-
`<style>${CONSOLE_STYLE}${graphOptionProvided ? GRAPH_STYLE : ""}${lifecycleEnabled ? LIFECYCLE_STYLE
|
|
7757
|
+
`<style>${CONSOLE_STYLE}${CONVERSATION_STYLE}${graphOptionProvided ? GRAPH_STYLE : ""}${lifecycleEnabled ? LIFECYCLE_STYLE : ""}</style>`,
|
|
7614
7758
|
"</head>",
|
|
7615
7759
|
"<body>",
|
|
7616
7760
|
'<div class="app">',
|
|
@@ -8387,7 +8531,8 @@ async function untilAborted(read, signal) {
|
|
|
8387
8531
|
}
|
|
8388
8532
|
async function handleConversationRoute(request, response, url, deps) {
|
|
8389
8533
|
const labelsOnly = url.pathname === "/api/graph/session-labels";
|
|
8390
|
-
|
|
8534
|
+
const direct = url.pathname === "/api/console/conversation";
|
|
8535
|
+
if (url.pathname !== "/api/graph/conversation" && !labelsOnly && !direct)
|
|
8391
8536
|
return false;
|
|
8392
8537
|
const send = (status, kind, message, extra = {}) => {
|
|
8393
8538
|
response.writeHead(status, {
|
|
@@ -8407,11 +8552,60 @@ async function handleConversationRoute(request, response, url, deps) {
|
|
|
8407
8552
|
send(405, "unavailable", consoleDetailMessages.unauthorized);
|
|
8408
8553
|
return true;
|
|
8409
8554
|
}
|
|
8410
|
-
if (!deps.enabled || !deps.contentEnabled
|
|
8555
|
+
if (!direct && !deps.enabled || !deps.contentEnabled) {
|
|
8411
8556
|
send(403, "disabled", consoleDetailMessages.disabled);
|
|
8412
8557
|
return true;
|
|
8413
8558
|
}
|
|
8414
8559
|
const params = url.searchParams;
|
|
8560
|
+
if (direct) {
|
|
8561
|
+
const sessionID = params.get("session");
|
|
8562
|
+
if (!sessionID || sessionID.length > 256 || params.getAll("session").length !== 1 || ["run", "operation", "attempt", "target"].some((key) => params.has(key))) {
|
|
8563
|
+
send(400, "invalid", consoleDetailMessages.invalid);
|
|
8564
|
+
return true;
|
|
8565
|
+
}
|
|
8566
|
+
if (params.has("cursor")) {
|
|
8567
|
+
send(409, "historical", consoleDetailMessages.historical);
|
|
8568
|
+
return true;
|
|
8569
|
+
}
|
|
8570
|
+
const controller2 = new AbortController;
|
|
8571
|
+
const cancel2 = () => controller2.abort();
|
|
8572
|
+
response.on("close", cancel2);
|
|
8573
|
+
request.on("aborted", cancel2);
|
|
8574
|
+
const timeout2 = setTimeout(cancel2, SESSION_HISTORY_LIMITS.timeoutMs);
|
|
8575
|
+
try {
|
|
8576
|
+
const observed = await untilAborted(deps.observedSessionIDs?.() ?? Promise.resolve([]), controller2.signal);
|
|
8577
|
+
if (!observed.includes(sessionID)) {
|
|
8578
|
+
send(404, "invalid", consoleDetailMessages.invalid);
|
|
8579
|
+
return true;
|
|
8580
|
+
}
|
|
8581
|
+
const endpoint = await untilAborted(deps.resolveEndpoint(sessionID), controller2.signal);
|
|
8582
|
+
if (!endpoint) {
|
|
8583
|
+
send(503, "unavailable", consoleDetailMessages.unavailable);
|
|
8584
|
+
return true;
|
|
8585
|
+
}
|
|
8586
|
+
const conversation = await untilAborted(deps.readConversation(endpoint, sessionID, controller2.signal), controller2.signal);
|
|
8587
|
+
send(200, conversation.messages.length || conversation.truncated ? "conversation" : "empty", `${consoleDetailMessages.current}${conversation.truncated ? `
|
|
8588
|
+
${consoleDetailMessages.truncated}` : conversation.messages.length ? "" : `
|
|
8589
|
+
${consoleDetailMessages.empty}`}`, {
|
|
8590
|
+
scope: "present-day-session",
|
|
8591
|
+
target: "session",
|
|
8592
|
+
sessionID,
|
|
8593
|
+
...conversation
|
|
8594
|
+
});
|
|
8595
|
+
} catch (error) {
|
|
8596
|
+
if (!response.destroyed)
|
|
8597
|
+
send(error instanceof ConversationTooLargeError ? 413 : 503, error instanceof ConversationTooLargeError ? "oversized" : "unavailable", error instanceof ConversationTooLargeError ? consoleDetailMessages.oversized : consoleDetailMessages.unavailable);
|
|
8598
|
+
} finally {
|
|
8599
|
+
clearTimeout(timeout2);
|
|
8600
|
+
response.off("close", cancel2);
|
|
8601
|
+
request.off("aborted", cancel2);
|
|
8602
|
+
}
|
|
8603
|
+
return true;
|
|
8604
|
+
}
|
|
8605
|
+
if (!deps.reader) {
|
|
8606
|
+
send(503, "unavailable", consoleDetailMessages.unavailable);
|
|
8607
|
+
return true;
|
|
8608
|
+
}
|
|
8415
8609
|
const rootTarget = params.get("target") === "root";
|
|
8416
8610
|
if ((labelsOnly ? params.has("run") ? ["run"] : [] : rootTarget ? ["run", "operation"] : ["run", "operation", "attempt"]).some((key) => params.getAll(key).length !== 1 || !params.get(key)) || params.has("session") || params.getAll("target").length > 1 || params.has("target") && !rootTarget && params.get("target") !== "attempt" || rootTarget && params.has("attempt") || labelsOnly && (params.has("operation") || params.has("attempt") || params.has("target"))) {
|
|
8417
8611
|
send(400, "invalid", consoleDetailMessages.invalid);
|
|
@@ -8435,7 +8629,7 @@ async function handleConversationRoute(request, response, url, deps) {
|
|
|
8435
8629
|
if (request.aborted || request.socket.destroyed)
|
|
8436
8630
|
cancel2();
|
|
8437
8631
|
const deadline = Date.now() + SESSION_HISTORY_LIMITS.timeoutMs;
|
|
8438
|
-
const
|
|
8632
|
+
const timeout2 = setTimeout(cancel2, SESSION_HISTORY_LIMITS.timeoutMs);
|
|
8439
8633
|
try {
|
|
8440
8634
|
const history2 = selected?.kind === "view" ? selected : undefined;
|
|
8441
8635
|
const selectedRoot = history2?.runs.find((run) => run.runID === history2.selection.runID)?.rootSessionID;
|
|
@@ -8501,7 +8695,7 @@ ${consoleDetailMessages.partialMetadata}` : consoleDetailMessages.currentMetadat
|
|
|
8501
8695
|
if (!response.destroyed && !request.socket.destroyed)
|
|
8502
8696
|
send(503, "unavailable", consoleDetailMessages.unavailable);
|
|
8503
8697
|
} finally {
|
|
8504
|
-
clearTimeout(
|
|
8698
|
+
clearTimeout(timeout2);
|
|
8505
8699
|
response.off("close", cancel2);
|
|
8506
8700
|
request.off("aborted", cancel2);
|
|
8507
8701
|
request.socket.off("close", cancel2);
|
|
@@ -8526,21 +8720,23 @@ ${consoleDetailMessages.partialMetadata}` : consoleDetailMessages.currentMetadat
|
|
|
8526
8720
|
const controller = new AbortController;
|
|
8527
8721
|
const cancel = () => controller.abort();
|
|
8528
8722
|
response.on("close", cancel);
|
|
8723
|
+
request.on("aborted", cancel);
|
|
8724
|
+
const timeout = setTimeout(cancel, SESSION_HISTORY_LIMITS.timeoutMs);
|
|
8529
8725
|
try {
|
|
8530
8726
|
const run = history.runs.find((item) => item.runID === history.selection.runID);
|
|
8531
8727
|
const recordedRoot = run?.rootSessionID;
|
|
8532
8728
|
const sessionID = rootTarget ? recordedRoot : attempt?.sessionID;
|
|
8533
8729
|
const endpointID = sessionID ?? history.projection.operations.flatMap((op) => op.attempts).filter((item) => item.sessionID).at(-1)?.sessionID ?? "";
|
|
8534
|
-
const endpoint = await deps.resolveEndpoint(endpointID);
|
|
8730
|
+
const endpoint = await untilAborted(deps.resolveEndpoint(endpointID), controller.signal);
|
|
8535
8731
|
if (!endpoint) {
|
|
8536
8732
|
send(503, "unavailable", consoleDetailMessages.unavailable);
|
|
8537
8733
|
return true;
|
|
8538
8734
|
}
|
|
8539
|
-
const recovered = !sessionID && rootTarget && deps.readConversation.root ? await deps.readConversation.root(endpoint, history.projection, controller.signal) : undefined;
|
|
8735
|
+
const recovered = !sessionID && rootTarget && deps.readConversation.root ? await untilAborted(deps.readConversation.root(endpoint, history.projection, controller.signal), controller.signal) : undefined;
|
|
8540
8736
|
const resolvedID = sessionID ?? recovered?.session.id;
|
|
8541
8737
|
if (!resolvedID)
|
|
8542
8738
|
throw new AssociationError("unavailable");
|
|
8543
|
-
const conversation = recovered ?? await deps.readConversation(endpoint, resolvedID, controller.signal);
|
|
8739
|
+
const conversation = recovered ?? await untilAborted(deps.readConversation(endpoint, resolvedID, controller.signal), controller.signal);
|
|
8544
8740
|
const current = rootTarget ? `${consoleDetailMessages.rootCurrent}
|
|
8545
8741
|
${consoleDetailMessages.current}${recovered ? `
|
|
8546
8742
|
${consoleDetailMessages.recovered}` : ""}` : consoleDetailMessages.current;
|
|
@@ -8556,10 +8752,12 @@ ${consoleDetailMessages.empty}`, {
|
|
|
8556
8752
|
...conversation
|
|
8557
8753
|
});
|
|
8558
8754
|
} catch (error) {
|
|
8559
|
-
if (!
|
|
8755
|
+
if (!response.destroyed)
|
|
8560
8756
|
send(error instanceof ConversationTooLargeError ? 413 : 503, error instanceof ConversationTooLargeError ? "oversized" : "unavailable", error instanceof AssociationError ? error.message : error instanceof ConversationTooLargeError ? consoleDetailMessages.oversized : consoleDetailMessages.unavailable);
|
|
8561
8757
|
} finally {
|
|
8758
|
+
clearTimeout(timeout);
|
|
8562
8759
|
response.off("close", cancel);
|
|
8760
|
+
request.off("aborted", cancel);
|
|
8563
8761
|
}
|
|
8564
8762
|
return true;
|
|
8565
8763
|
}
|
|
@@ -8839,15 +9037,19 @@ async function createConsoleServer(deps) {
|
|
|
8839
9037
|
};
|
|
8840
9038
|
const handle = async (request, response) => {
|
|
8841
9039
|
const url2 = new URL(request.url ?? "/", "http://localhost");
|
|
8842
|
-
if ([
|
|
9040
|
+
if ([
|
|
9041
|
+
"/api/graph/conversation",
|
|
9042
|
+
"/api/graph/session-labels",
|
|
9043
|
+
"/api/console/conversation"
|
|
9044
|
+
].includes(url2.pathname) && deps.storageApi && !readAuthorized2(request, url2, deps.storageApi.token)) {
|
|
8843
9045
|
response.writeHead(401, { ...JSON_HEADERS5, "cache-control": "no-store" }).end(JSON.stringify({ version: 1, kind: "unauthorized" }));
|
|
8844
9046
|
return;
|
|
8845
9047
|
}
|
|
8846
|
-
if (deps.console?.readConversation &&
|
|
9048
|
+
if (deps.console?.readConversation && await handleConversationRoute(request, response, url2, {
|
|
8847
9049
|
token: deps.console.token,
|
|
8848
9050
|
enabled: deps.config.graphView?.enabled === true,
|
|
8849
|
-
contentEnabled: deps.console.conversationContentEnabled
|
|
8850
|
-
reader: deps.lifecycleHistoryApi.reader,
|
|
9051
|
+
contentEnabled: deps.console.conversationContentEnabled ?? deps.config.sessionHistory?.contentEnabled ?? true,
|
|
9052
|
+
...deps.lifecycleHistoryApi ? { reader: deps.lifecycleHistoryApi.reader } : {},
|
|
8851
9053
|
resolveEndpoint: async (id) => deps.console?.historyEndpoint ?? await resolveEndpoint(id),
|
|
8852
9054
|
observedSessionIDs: async () => buildConsoleState(await deps.readSnapshot()).sessions.map((session) => session.sessionID),
|
|
8853
9055
|
readConversation: deps.console.readConversation
|
|
@@ -9014,6 +9216,7 @@ var PROVIDER_LABELS = {
|
|
|
9014
9216
|
ollama: "Ollama",
|
|
9015
9217
|
lmstudio: "LM Studio",
|
|
9016
9218
|
lemonade: "Lemonade Server",
|
|
9219
|
+
"llama-swap": executionSetupMessages.llamaSwap.label,
|
|
9017
9220
|
"foundry-local": "Foundry Local"
|
|
9018
9221
|
};
|
|
9019
9222
|
function providerLabel(providerID) {
|
|
@@ -9235,7 +9438,8 @@ var KNOWN_LOCAL_PROVIDER_IDS = new Set([
|
|
|
9235
9438
|
"ollama",
|
|
9236
9439
|
"lmstudio",
|
|
9237
9440
|
"foundry-local",
|
|
9238
|
-
"lemonade"
|
|
9441
|
+
"lemonade",
|
|
9442
|
+
"llama-swap"
|
|
9239
9443
|
]);
|
|
9240
9444
|
var KNOWN_FRONTIER_PROVIDER_IDS = new Set([
|
|
9241
9445
|
"github-copilot",
|
|
@@ -11150,7 +11354,7 @@ async function runConsoleServe(deps, options) {
|
|
|
11150
11354
|
ptyEnabled,
|
|
11151
11355
|
createClient: makeClient,
|
|
11152
11356
|
drilldownEnabled: graphViewEnabled,
|
|
11153
|
-
conversationContentEnabled: config.console.sessionHistory?.contentEnabled
|
|
11357
|
+
conversationContentEnabled: config.console.sessionHistory?.contentEnabled !== false,
|
|
11154
11358
|
...config.console.sessionHistory?.endpoint !== undefined ? { historyEndpoint: config.console.sessionHistory.endpoint } : {},
|
|
11155
11359
|
readConversation: createConversationReader({
|
|
11156
11360
|
directory: deps.directory ?? process.cwd(),
|
|
@@ -16043,6 +16247,7 @@ var reasons = {
|
|
|
16043
16247
|
unknown: "fresh residency metadata is unavailable, malformed, or ambiguous",
|
|
16044
16248
|
inactive: "the requested model is not verified as resident and serving",
|
|
16045
16249
|
identity: "the requested model identity can be rewritten or is ambiguous",
|
|
16250
|
+
"authentication-unsupported": "authentication is required but unsupported by this integration",
|
|
16046
16251
|
destination: "the request destination does not match its configured runtime",
|
|
16047
16252
|
transport: "this provider execution path cannot enforce the residency guard",
|
|
16048
16253
|
request: "this is not a supported, explicit model inference request",
|
|
@@ -16051,22 +16256,72 @@ var reasons = {
|
|
|
16051
16256
|
configuration: "the residency guard configuration is invalid"
|
|
16052
16257
|
};
|
|
16053
16258
|
var modelResidencyMessages = {
|
|
16054
|
-
blocked: (reason) => `[openteam]
|
|
16055
|
-
providerDisabled: (providerID) => `[openteam]
|
|
16056
|
-
extractionRuntimeUnavailable: "[openteam] Memory extraction requires one enabled local runtime matching the configured extraction provider."
|
|
16259
|
+
blocked: (reason, runtimeLabel = "Lemonade", detail) => `[openteam] ${runtimeLabel} model request blocked: ${reasons[reason]}${detail === undefined ? "" : ` (${detail})`}.`,
|
|
16260
|
+
providerDisabled: (providerID, runtimeLabel = "Lemonade") => `[openteam] ${runtimeLabel} provider "${providerID}" is disabled because its configured transport or runtime mapping cannot enforce loaded-only requests.`,
|
|
16261
|
+
extractionRuntimeUnavailable: "[openteam] Memory extraction requires one enabled local runtime matching the configured extraction provider.",
|
|
16262
|
+
extractionRuntimeUnavailableFor: (runtimeLabel) => `[openteam] Memory extraction requires one enabled ${runtimeLabel} local runtime matching the configured extraction provider.`
|
|
16263
|
+
};
|
|
16264
|
+
var llamaSwapResidencyMessages = {
|
|
16265
|
+
catalogModelDetail: (id, detail) => `catalog model "${id}" ${detail}`,
|
|
16266
|
+
invalidCatalogField: (field) => `declares an invalid ${field}`,
|
|
16267
|
+
catalogEntryMustExposeStringId: "catalog entries must expose a string id",
|
|
16268
|
+
catalogModelIdsMustBeNonEmptyTrimmedStrings: "catalog model ids must be non-empty, trimmed strings",
|
|
16269
|
+
expectedDataArray: (path) => `GET ${path} must return an object with a data array`,
|
|
16270
|
+
duplicateCanonicalModelId: (modelID, path) => `duplicate canonical model id "${modelID}" in ${path}`,
|
|
16271
|
+
httpFailure: (path, status) => `GET ${path} failed with HTTP ${status}`,
|
|
16272
|
+
unverifiableResponse: (path) => `GET ${path} failed before a verifiable response`,
|
|
16273
|
+
expectedProfilesArray: (path) => `GET ${path} must return an object with a profiles array`,
|
|
16274
|
+
activeProfileRewrite: (profile) => `active profile "${profile}" can rewrite canonical model routing`,
|
|
16275
|
+
invalidActiveProfileValue: (path) => `GET ${path} reported an invalid active profile value`,
|
|
16276
|
+
expectedRunningArray: (path) => `GET ${path} must return an object with a running array`,
|
|
16277
|
+
malformedRunningEvidence: (path) => `GET ${path} returned malformed model/state evidence`,
|
|
16278
|
+
duplicateRunningEvidence: (path, modelID) => `GET ${path} returned duplicate evidence for "${modelID}"`
|
|
16057
16279
|
};
|
|
16058
16280
|
|
|
16059
|
-
// src/local/
|
|
16281
|
+
// src/local/residency.ts
|
|
16060
16282
|
class ModelResidencyError extends Error {
|
|
16061
16283
|
code;
|
|
16062
16284
|
statusCode = 400;
|
|
16063
16285
|
isRetryable = false;
|
|
16064
|
-
constructor(code) {
|
|
16065
|
-
super(modelResidencyMessages.blocked(code));
|
|
16286
|
+
constructor(code, runtimeLabel = "Lemonade", detail) {
|
|
16287
|
+
super(modelResidencyMessages.blocked(code, runtimeLabel, detail));
|
|
16066
16288
|
this.code = code;
|
|
16067
16289
|
this.name = "ModelResidencyError";
|
|
16068
16290
|
}
|
|
16069
16291
|
}
|
|
16292
|
+
async function withResidencyDeadline(operation, options) {
|
|
16293
|
+
const timeoutMs = options.timeoutMs ?? 5000;
|
|
16294
|
+
const runtimeLabel = options.runtimeLabel;
|
|
16295
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
16296
|
+
throw new ModelResidencyError("configuration", runtimeLabel);
|
|
16297
|
+
}
|
|
16298
|
+
const controller = new AbortController;
|
|
16299
|
+
const cancel = () => controller.abort(new ModelResidencyError("cancelled", runtimeLabel));
|
|
16300
|
+
if (options.signal?.aborted)
|
|
16301
|
+
throw new ModelResidencyError("cancelled", runtimeLabel);
|
|
16302
|
+
options.signal?.addEventListener("abort", cancel, { once: true });
|
|
16303
|
+
const schedule = options.scheduleTimeout ?? ((callback, ms2) => {
|
|
16304
|
+
const timer = setTimeout(callback, ms2);
|
|
16305
|
+
return () => clearTimeout(timer);
|
|
16306
|
+
});
|
|
16307
|
+
let onAbort = () => {};
|
|
16308
|
+
const aborted = new Promise((_, reject) => {
|
|
16309
|
+
onAbort = () => reject(controller.signal.reason);
|
|
16310
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
16311
|
+
});
|
|
16312
|
+
const clearTimer = schedule(() => controller.abort(new ModelResidencyError("timeout", runtimeLabel)), timeoutMs);
|
|
16313
|
+
try {
|
|
16314
|
+
controller.signal.throwIfAborted();
|
|
16315
|
+
return await Promise.race([operation(controller.signal), aborted]);
|
|
16316
|
+
} finally {
|
|
16317
|
+
clearTimer();
|
|
16318
|
+
options.signal?.removeEventListener("abort", cancel);
|
|
16319
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
16320
|
+
controller.abort();
|
|
16321
|
+
}
|
|
16322
|
+
}
|
|
16323
|
+
|
|
16324
|
+
// src/local/lemonadeResidency.ts
|
|
16070
16325
|
var modelName = z22.string().min(1).refine((name) => name === name.trim());
|
|
16071
16326
|
var LoadedModelSchema = z22.object({
|
|
16072
16327
|
model_name: modelName,
|
|
@@ -16203,36 +16458,6 @@ function lemonadeBaseURL(value) {
|
|
|
16203
16458
|
}
|
|
16204
16459
|
return `${url.origin}${url.pathname.replace(/\/$/, "")}`;
|
|
16205
16460
|
}
|
|
16206
|
-
async function withResidencyDeadline(operation, options) {
|
|
16207
|
-
const timeoutMs = options.timeoutMs ?? 5000;
|
|
16208
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
16209
|
-
throw new ModelResidencyError("configuration");
|
|
16210
|
-
}
|
|
16211
|
-
const controller = new AbortController;
|
|
16212
|
-
const cancel = () => controller.abort(new ModelResidencyError("cancelled"));
|
|
16213
|
-
if (options.signal?.aborted)
|
|
16214
|
-
throw new ModelResidencyError("cancelled");
|
|
16215
|
-
options.signal?.addEventListener("abort", cancel, { once: true });
|
|
16216
|
-
const schedule = options.scheduleTimeout ?? ((callback, ms2) => {
|
|
16217
|
-
const timer = setTimeout(callback, ms2);
|
|
16218
|
-
return () => clearTimeout(timer);
|
|
16219
|
-
});
|
|
16220
|
-
let onAbort = () => {};
|
|
16221
|
-
const aborted = new Promise((_, reject) => {
|
|
16222
|
-
onAbort = () => reject(controller.signal.reason);
|
|
16223
|
-
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
16224
|
-
});
|
|
16225
|
-
const clearTimer = schedule(() => controller.abort(new ModelResidencyError("timeout")), timeoutMs);
|
|
16226
|
-
try {
|
|
16227
|
-
controller.signal.throwIfAborted();
|
|
16228
|
-
return await Promise.race([operation(controller.signal), aborted]);
|
|
16229
|
-
} finally {
|
|
16230
|
-
clearTimer();
|
|
16231
|
-
options.signal?.removeEventListener("abort", cancel);
|
|
16232
|
-
controller.signal.removeEventListener("abort", onAbort);
|
|
16233
|
-
controller.abort();
|
|
16234
|
-
}
|
|
16235
|
-
}
|
|
16236
16461
|
async function metadata(url, fetch2, signal, headers) {
|
|
16237
16462
|
signal.throwIfAborted();
|
|
16238
16463
|
let payload;
|
|
@@ -16536,9 +16761,314 @@ function isObject2(value) {
|
|
|
16536
16761
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16537
16762
|
}
|
|
16538
16763
|
|
|
16764
|
+
// src/local/llamaSwapResidency.ts
|
|
16765
|
+
function isObject3(value) {
|
|
16766
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16767
|
+
}
|
|
16768
|
+
function readPositiveSafeInteger(value) {
|
|
16769
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
|
|
16770
|
+
}
|
|
16771
|
+
function readSupportsTools2(model) {
|
|
16772
|
+
const direct = readBoolean2(model, [
|
|
16773
|
+
"tool_call",
|
|
16774
|
+
"tool_calls",
|
|
16775
|
+
"toolCalling",
|
|
16776
|
+
"supportsToolCalling",
|
|
16777
|
+
"supports_tools",
|
|
16778
|
+
"supportsTools"
|
|
16779
|
+
]);
|
|
16780
|
+
if (direct !== undefined) {
|
|
16781
|
+
return direct;
|
|
16782
|
+
}
|
|
16783
|
+
const capabilities = readCapabilityBoolean2(model.capabilities);
|
|
16784
|
+
if (capabilities !== undefined) {
|
|
16785
|
+
return capabilities;
|
|
16786
|
+
}
|
|
16787
|
+
const features = readCapabilityBoolean2(model.features);
|
|
16788
|
+
if (features !== undefined) {
|
|
16789
|
+
return features;
|
|
16790
|
+
}
|
|
16791
|
+
const labels = readCapabilityBoolean2(model.labels);
|
|
16792
|
+
if (labels !== undefined) {
|
|
16793
|
+
return labels;
|
|
16794
|
+
}
|
|
16795
|
+
return "unknown";
|
|
16796
|
+
}
|
|
16797
|
+
function readCapabilityBoolean2(value) {
|
|
16798
|
+
if (Array.isArray(value)) {
|
|
16799
|
+
if (value.some((entry) => typeof entry === "string" && ["tools", "tool_call", "tool-calling", "function_calling"].includes(entry))) {
|
|
16800
|
+
return true;
|
|
16801
|
+
}
|
|
16802
|
+
return;
|
|
16803
|
+
}
|
|
16804
|
+
if (!isObject3(value)) {
|
|
16805
|
+
return;
|
|
16806
|
+
}
|
|
16807
|
+
return readBoolean2(value, [
|
|
16808
|
+
"tools",
|
|
16809
|
+
"tool_call",
|
|
16810
|
+
"tool_calls",
|
|
16811
|
+
"toolCalling",
|
|
16812
|
+
"function_calling",
|
|
16813
|
+
"supportsToolCalling"
|
|
16814
|
+
]);
|
|
16815
|
+
}
|
|
16816
|
+
function readBoolean2(value, keys) {
|
|
16817
|
+
for (const key of keys) {
|
|
16818
|
+
if (typeof value[key] === "boolean") {
|
|
16819
|
+
return value[key];
|
|
16820
|
+
}
|
|
16821
|
+
}
|
|
16822
|
+
return;
|
|
16823
|
+
}
|
|
16824
|
+
function modelDetail(id, detail) {
|
|
16825
|
+
return llamaSwapResidencyMessages.catalogModelDetail(id, detail);
|
|
16826
|
+
}
|
|
16827
|
+
function llamaSwapBaseURL(value) {
|
|
16828
|
+
let url;
|
|
16829
|
+
try {
|
|
16830
|
+
url = new URL(value);
|
|
16831
|
+
} catch {
|
|
16832
|
+
throw new ModelResidencyError("destination", "llama-swap");
|
|
16833
|
+
}
|
|
16834
|
+
if (url.protocol !== "http:" && url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") {
|
|
16835
|
+
throw new ModelResidencyError("destination", "llama-swap");
|
|
16836
|
+
}
|
|
16837
|
+
if (url.pathname === "/" || url.pathname === "") {
|
|
16838
|
+
return `${url.origin}/v1`;
|
|
16839
|
+
}
|
|
16840
|
+
if (url.pathname === "/v1" || url.pathname === "/v1/") {
|
|
16841
|
+
return `${url.origin}/v1`;
|
|
16842
|
+
}
|
|
16843
|
+
throw new ModelResidencyError("destination", "llama-swap");
|
|
16844
|
+
}
|
|
16845
|
+
function parseCanonicalType(entry, id) {
|
|
16846
|
+
if (!isObject3(entry.meta) || !isObject3(entry.meta.llamaswap)) {
|
|
16847
|
+
return;
|
|
16848
|
+
}
|
|
16849
|
+
const type = entry.meta.llamaswap.type;
|
|
16850
|
+
if (type === undefined) {
|
|
16851
|
+
return;
|
|
16852
|
+
}
|
|
16853
|
+
if (typeof type !== "string" || type.length === 0 || type !== type.trim()) {
|
|
16854
|
+
throw new ModelResidencyError("unknown", "llama-swap", modelDetail(id, llamaSwapResidencyMessages.invalidCatalogField("meta.llamaswap.type")));
|
|
16855
|
+
}
|
|
16856
|
+
return type === "model" ? "model" : undefined;
|
|
16857
|
+
}
|
|
16858
|
+
function readContextCandidates(entry) {
|
|
16859
|
+
const values = [
|
|
16860
|
+
readPositiveSafeInteger(entry.context_length),
|
|
16861
|
+
readPositiveSafeInteger(entry.context_window),
|
|
16862
|
+
isObject3(entry.meta) ? readPositiveSafeInteger(entry.meta.n_ctx) : undefined
|
|
16863
|
+
];
|
|
16864
|
+
return values.filter((value) => value !== undefined);
|
|
16865
|
+
}
|
|
16866
|
+
function readDeclaredMaxOutput(entry, id) {
|
|
16867
|
+
if (!isObject3(entry.meta) || !isObject3(entry.meta.llamaswap)) {
|
|
16868
|
+
return;
|
|
16869
|
+
}
|
|
16870
|
+
if (!Object.hasOwn(entry.meta.llamaswap, "max_output_tokens")) {
|
|
16871
|
+
return;
|
|
16872
|
+
}
|
|
16873
|
+
const maxOutputTokens = readPositiveSafeInteger(entry.meta.llamaswap.max_output_tokens);
|
|
16874
|
+
if (maxOutputTokens === undefined) {
|
|
16875
|
+
throw new ModelResidencyError("unknown", "llama-swap", modelDetail(id, llamaSwapResidencyMessages.invalidCatalogField("meta.llamaswap.max_output_tokens")));
|
|
16876
|
+
}
|
|
16877
|
+
return maxOutputTokens;
|
|
16878
|
+
}
|
|
16879
|
+
function parseCatalogModel(value) {
|
|
16880
|
+
if (!isObject3(value) || typeof value.id !== "string") {
|
|
16881
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.catalogEntryMustExposeStringId);
|
|
16882
|
+
}
|
|
16883
|
+
const id = value.id;
|
|
16884
|
+
if (id.length === 0 || id !== id.trim()) {
|
|
16885
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.catalogModelIdsMustBeNonEmptyTrimmedStrings);
|
|
16886
|
+
}
|
|
16887
|
+
if (parseCanonicalType(value, id) !== "model") {
|
|
16888
|
+
return;
|
|
16889
|
+
}
|
|
16890
|
+
const model = {
|
|
16891
|
+
modelID: id,
|
|
16892
|
+
supportsTools: readSupportsTools2(value)
|
|
16893
|
+
};
|
|
16894
|
+
const contexts = readContextCandidates(value);
|
|
16895
|
+
if (contexts.length > 0) {
|
|
16896
|
+
model.contextWindow = Math.min(...contexts);
|
|
16897
|
+
model.contextWindowProvenance = "declared";
|
|
16898
|
+
}
|
|
16899
|
+
const maxOutputTokens = readDeclaredMaxOutput(value, id);
|
|
16900
|
+
if (maxOutputTokens !== undefined) {
|
|
16901
|
+
model.maxOutputTokens = maxOutputTokens;
|
|
16902
|
+
model.maxOutputTokensProvenance = "declared";
|
|
16903
|
+
}
|
|
16904
|
+
return model;
|
|
16905
|
+
}
|
|
16906
|
+
function parseCatalog(payload) {
|
|
16907
|
+
if (!isObject3(payload) || !Array.isArray(payload.data)) {
|
|
16908
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.expectedDataArray("/v1/models"));
|
|
16909
|
+
}
|
|
16910
|
+
const seen = new Set;
|
|
16911
|
+
const models = [];
|
|
16912
|
+
for (const entry of payload.data) {
|
|
16913
|
+
const model = parseCatalogModel(entry);
|
|
16914
|
+
if (model === undefined) {
|
|
16915
|
+
continue;
|
|
16916
|
+
}
|
|
16917
|
+
if (seen.has(model.modelID)) {
|
|
16918
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.duplicateCanonicalModelId(model.modelID, "/v1/models"));
|
|
16919
|
+
}
|
|
16920
|
+
seen.add(model.modelID);
|
|
16921
|
+
models.push(model);
|
|
16922
|
+
}
|
|
16923
|
+
return models;
|
|
16924
|
+
}
|
|
16925
|
+
async function metadata2(url, fetch2, signal, headers) {
|
|
16926
|
+
signal.throwIfAborted();
|
|
16927
|
+
try {
|
|
16928
|
+
const response = await fetch2(url, {
|
|
16929
|
+
method: "GET",
|
|
16930
|
+
headers,
|
|
16931
|
+
signal,
|
|
16932
|
+
redirect: "error",
|
|
16933
|
+
cache: "no-store"
|
|
16934
|
+
});
|
|
16935
|
+
signal.throwIfAborted();
|
|
16936
|
+
if (response.status === 401 || response.status === 403) {
|
|
16937
|
+
throw new ModelResidencyError("authentication-unsupported", "llama-swap");
|
|
16938
|
+
}
|
|
16939
|
+
if (!response.ok || response.redirected || response.url !== "" && response.url !== url) {
|
|
16940
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.httpFailure(new URL(url).pathname, response.status));
|
|
16941
|
+
}
|
|
16942
|
+
return await response.json();
|
|
16943
|
+
} catch (error) {
|
|
16944
|
+
signal.throwIfAborted();
|
|
16945
|
+
if (error instanceof ModelResidencyError) {
|
|
16946
|
+
throw error;
|
|
16947
|
+
}
|
|
16948
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.unverifiableResponse(new URL(url).pathname));
|
|
16949
|
+
}
|
|
16950
|
+
}
|
|
16951
|
+
function readProfiles(payload) {
|
|
16952
|
+
if (!isObject3(payload) || !Array.isArray(payload.profiles)) {
|
|
16953
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.expectedProfilesArray("/api/profiles"));
|
|
16954
|
+
}
|
|
16955
|
+
if (payload.active === null) {
|
|
16956
|
+
return;
|
|
16957
|
+
}
|
|
16958
|
+
if (typeof payload.active === "string" && payload.active.length > 0 && payload.active === payload.active.trim()) {
|
|
16959
|
+
throw new ModelResidencyError("identity", "llama-swap", llamaSwapResidencyMessages.activeProfileRewrite(payload.active));
|
|
16960
|
+
}
|
|
16961
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.invalidActiveProfileValue("/api/profiles"));
|
|
16962
|
+
}
|
|
16963
|
+
function readRunning(payload, modelIDs) {
|
|
16964
|
+
if (!isObject3(payload) || !Array.isArray(payload.running)) {
|
|
16965
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.expectedRunningArray("/running"));
|
|
16966
|
+
}
|
|
16967
|
+
const result = new Map;
|
|
16968
|
+
for (const modelID of modelIDs) {
|
|
16969
|
+
result.set(modelID, { loaded: false });
|
|
16970
|
+
}
|
|
16971
|
+
const seen = new Set;
|
|
16972
|
+
for (const entry of payload.running) {
|
|
16973
|
+
if (!isObject3(entry) || typeof entry.model !== "string" || entry.model.length === 0 || entry.model !== entry.model.trim() || typeof entry.state !== "string" || entry.state.length === 0 || entry.state !== entry.state.trim()) {
|
|
16974
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.malformedRunningEvidence("/running"));
|
|
16975
|
+
}
|
|
16976
|
+
if (seen.has(entry.model)) {
|
|
16977
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.duplicateRunningEvidence("/running", entry.model));
|
|
16978
|
+
}
|
|
16979
|
+
seen.add(entry.model);
|
|
16980
|
+
if (!modelIDs.has(entry.model)) {
|
|
16981
|
+
continue;
|
|
16982
|
+
}
|
|
16983
|
+
result.set(entry.model, { loaded: entry.state === "ready" });
|
|
16984
|
+
}
|
|
16985
|
+
return result;
|
|
16986
|
+
}
|
|
16987
|
+
function requestHeaders(headers) {
|
|
16988
|
+
const result = new Headers(headers);
|
|
16989
|
+
result.delete("content-type");
|
|
16990
|
+
result.delete("content-length");
|
|
16991
|
+
result.set("accept", "application/json");
|
|
16992
|
+
result.set("cache-control", "no-cache, no-store");
|
|
16993
|
+
result.delete("if-none-match");
|
|
16994
|
+
result.delete("if-modified-since");
|
|
16995
|
+
return result;
|
|
16996
|
+
}
|
|
16997
|
+
function llamaSwapResidencyOptions(options) {
|
|
16998
|
+
return { ...options, runtimeLabel: "llama-swap" };
|
|
16999
|
+
}
|
|
17000
|
+
async function readLlamaSwapCatalog(baseURL, fetch2, options = {}) {
|
|
17001
|
+
const base = llamaSwapBaseURL(baseURL);
|
|
17002
|
+
const headers = requestHeaders(options.headers);
|
|
17003
|
+
return withResidencyDeadline(async (signal) => parseCatalog(await metadata2(`${base}/models`, fetch2, signal, headers)), llamaSwapResidencyOptions(options));
|
|
17004
|
+
}
|
|
17005
|
+
async function readLlamaSwapResidency(baseURL, fetch2, options = {}) {
|
|
17006
|
+
const base = llamaSwapBaseURL(baseURL);
|
|
17007
|
+
const origin = new URL(base).origin;
|
|
17008
|
+
const headers = requestHeaders(options.headers);
|
|
17009
|
+
return withResidencyDeadline(async (signal) => {
|
|
17010
|
+
const models = await readLlamaSwapCatalog(base, fetch2, {
|
|
17011
|
+
...llamaSwapResidencyOptions(options),
|
|
17012
|
+
signal
|
|
17013
|
+
});
|
|
17014
|
+
readProfiles(await metadata2(`${origin}/api/profiles`, fetch2, signal, headers));
|
|
17015
|
+
return readRunning(await metadata2(`${origin}/running`, fetch2, signal, headers), new Set(models.map((model) => model.modelID)));
|
|
17016
|
+
}, llamaSwapResidencyOptions(options));
|
|
17017
|
+
}
|
|
17018
|
+
|
|
17019
|
+
// src/local/llamaSwap.ts
|
|
17020
|
+
var LLAMA_SWAP_DEFAULT_BASE_URL = "http://localhost:8080/v1";
|
|
17021
|
+
function withLoadedState(models, residency) {
|
|
17022
|
+
return models.map((model) => {
|
|
17023
|
+
const loaded = residency.get(model.modelID)?.loaded;
|
|
17024
|
+
return loaded === undefined ? model : { ...model, loaded };
|
|
17025
|
+
});
|
|
17026
|
+
}
|
|
17027
|
+
function createLlamaSwapAdapter() {
|
|
17028
|
+
const listModels = async (options) => {
|
|
17029
|
+
const baseURL = llamaSwapBaseURL(options.baseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL);
|
|
17030
|
+
const models = await readLlamaSwapCatalog(baseURL, options.fetch);
|
|
17031
|
+
try {
|
|
17032
|
+
return withLoadedState(models, await readLlamaSwapResidency(baseURL, options.fetch));
|
|
17033
|
+
} catch (error) {
|
|
17034
|
+
if (error instanceof ModelResidencyError && (error.code === "identity" || error.code === "unknown")) {
|
|
17035
|
+
return models;
|
|
17036
|
+
}
|
|
17037
|
+
throw error;
|
|
17038
|
+
}
|
|
17039
|
+
};
|
|
17040
|
+
return {
|
|
17041
|
+
id: "llama-swap",
|
|
17042
|
+
defaultBaseURL: LLAMA_SWAP_DEFAULT_BASE_URL,
|
|
17043
|
+
listModels,
|
|
17044
|
+
async probe(options) {
|
|
17045
|
+
const baseURL = llamaSwapBaseURL(options.baseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL);
|
|
17046
|
+
try {
|
|
17047
|
+
return {
|
|
17048
|
+
id: "llama-swap",
|
|
17049
|
+
baseURL,
|
|
17050
|
+
reachable: true,
|
|
17051
|
+
models: await listModels({ ...options, baseURL }),
|
|
17052
|
+
probedAt: options.probedAt
|
|
17053
|
+
};
|
|
17054
|
+
} catch (error) {
|
|
17055
|
+
return unavailableSnapshot({
|
|
17056
|
+
id: "llama-swap",
|
|
17057
|
+
baseURL,
|
|
17058
|
+
probedAt: options.probedAt,
|
|
17059
|
+
error
|
|
17060
|
+
});
|
|
17061
|
+
}
|
|
17062
|
+
}
|
|
17063
|
+
};
|
|
17064
|
+
}
|
|
17065
|
+
|
|
16539
17066
|
// src/local/modelResidency.ts
|
|
17067
|
+
function runtimeRequiresModelResidency(kind) {
|
|
17068
|
+
return kind === "lemonade" || kind === "llama-swap";
|
|
17069
|
+
}
|
|
16540
17070
|
function hasRequiredModelResidency(kind, model) {
|
|
16541
|
-
return kind
|
|
17071
|
+
return !runtimeRequiresModelResidency(kind) || model?.loaded === true;
|
|
16542
17072
|
}
|
|
16543
17073
|
|
|
16544
17074
|
// src/local/lmstudio.ts
|
|
@@ -16593,6 +17123,7 @@ class RuntimeRegistry {
|
|
|
16593
17123
|
lmstudio: createLMStudioAdapter(),
|
|
16594
17124
|
"foundry-local": createFoundryLocalAdapter(),
|
|
16595
17125
|
lemonade: createLemonadeAdapter(),
|
|
17126
|
+
"llama-swap": createLlamaSwapAdapter(),
|
|
16596
17127
|
...options.adapters
|
|
16597
17128
|
};
|
|
16598
17129
|
this.clock = options.clock;
|
|
@@ -16730,6 +17261,12 @@ var DETECTION_RUNTIMES = [
|
|
|
16730
17261
|
baseURL: LEMONADE_DEFAULT_BASE_URL,
|
|
16731
17262
|
defaultModel: { providerID: "lemonade", modelID: "probe" }
|
|
16732
17263
|
},
|
|
17264
|
+
{
|
|
17265
|
+
id: "llama-swap",
|
|
17266
|
+
enabled: true,
|
|
17267
|
+
baseURL: LLAMA_SWAP_DEFAULT_BASE_URL,
|
|
17268
|
+
defaultModel: { providerID: "llama-swap", modelID: "probe" }
|
|
17269
|
+
},
|
|
16733
17270
|
{
|
|
16734
17271
|
id: "foundry-local",
|
|
16735
17272
|
enabled: true,
|
|
@@ -17228,7 +17765,7 @@ function runtimeLine(snapshot, runtime) {
|
|
|
17228
17765
|
const mark = snapshot.reachable ? "✓" : "✗";
|
|
17229
17766
|
const detail = snapshot.reachable ? `${snapshot.models.length} model(s)` : snapshot.error ?? "unreachable";
|
|
17230
17767
|
const declared = runtime?.maxConcurrency;
|
|
17231
|
-
const slots = declared === undefined ?
|
|
17768
|
+
const slots = declared === undefined ? doctorMessages.defaultRuntimeSlots(effectiveMaxConcurrency(runtime ?? {})) : ` · ${declared} slot(s) (declared)`;
|
|
17232
17769
|
return ` ${mark} ${snapshot.id.padEnd(14)} ${snapshot.baseURL || "(no baseURL)"} — ${detail}${slots}`;
|
|
17233
17770
|
}
|
|
17234
17771
|
function runtimeModelSlotsLine(runtime, snapshot) {
|
|
@@ -17240,6 +17777,8 @@ function runtimeModelSlotsLine(runtime, snapshot) {
|
|
|
17240
17777
|
if (runtime === undefined || snapshot === undefined || !snapshot.reachable || snapshot.models.length === 0) {
|
|
17241
17778
|
return [];
|
|
17242
17779
|
}
|
|
17780
|
+
if (localRuntimeKind(runtime) === "llama-swap")
|
|
17781
|
+
return [doctorMessages.llamaSwapConcurrency];
|
|
17243
17782
|
const reported = snapshot.models.flatMap((model) => model.slots === undefined ? [] : [`${model.modelID} ${model.slots} slot(s)`]);
|
|
17244
17783
|
if (reported.length > 0) {
|
|
17245
17784
|
return [
|
|
@@ -17643,7 +18182,7 @@ function renderDoctor(input) {
|
|
|
17643
18182
|
continue;
|
|
17644
18183
|
}
|
|
17645
18184
|
const runtime = enabledRuntimes.find((r) => r.id === snapshot.id);
|
|
17646
|
-
const cap = runtime
|
|
18185
|
+
const cap = effectiveMaxConcurrency(runtime ?? {});
|
|
17647
18186
|
lines.push(` ⚠ ${snapshot.id} is reached over the network: its ${cap}-slot cap is enforced per openteam process, so`, " two processes at once (an opencode session plus 'openteam console') can together exceed it.");
|
|
17648
18187
|
}
|
|
17649
18188
|
lines.push(` telemetry: ${input.telemetryPath} — ${input.telemetryRecords} record(s)`);
|
|
@@ -18046,7 +18585,7 @@ import { posix as pathPosix, win32 as pathWin32 } from "node:path";
|
|
|
18046
18585
|
// package.json
|
|
18047
18586
|
var package_default = {
|
|
18048
18587
|
name: "@jmanuelcorral/openteam",
|
|
18049
|
-
version: "0.
|
|
18588
|
+
version: "0.26.0",
|
|
18050
18589
|
packageManager: "bun@1.3.14",
|
|
18051
18590
|
description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
|
|
18052
18591
|
license: "MIT",
|
|
@@ -18365,14 +18904,25 @@ function frontierModelsForProvider(profiles, providerID) {
|
|
|
18365
18904
|
var OPENCODE_GITIGNORE_PATH = ".opencode/.gitignore";
|
|
18366
18905
|
var DEFAULT_LOCAL_OUTPUT_TOKENS = 8192;
|
|
18367
18906
|
var MIN_USABLE_INPUT_TOKENS = 1024;
|
|
18368
|
-
function computeModelLimit(contextWindow, inputBudget) {
|
|
18907
|
+
function computeModelLimit(contextWindow, inputBudget, maxOutputTokens) {
|
|
18369
18908
|
const context = contextWindow ?? 0;
|
|
18370
18909
|
const contextBudget = context > 0 ? context : undefined;
|
|
18371
18910
|
const validInputBudget = inputBudget !== undefined && inputBudget > 0 ? inputBudget : undefined;
|
|
18372
18911
|
const effectiveBudget = contextBudget === undefined ? validInputBudget : validInputBudget === undefined ? contextBudget : Math.min(contextBudget, validInputBudget);
|
|
18373
|
-
const
|
|
18912
|
+
const desired = Math.min(DEFAULT_LOCAL_OUTPUT_TOKENS, positiveInteger3(maxOutputTokens) ?? DEFAULT_LOCAL_OUTPUT_TOKENS);
|
|
18913
|
+
const output = Math.min(desired, effectiveBudget !== undefined && effectiveBudget - desired < MIN_USABLE_INPUT_TOKENS ? Math.max(1, Math.floor(effectiveBudget / 2)) : desired);
|
|
18374
18914
|
return { context, output };
|
|
18375
18915
|
}
|
|
18916
|
+
function collectMaxOutputTokens(modelInfo, usedModelIDs) {
|
|
18917
|
+
const used = new Set(usedModelIDs);
|
|
18918
|
+
const ceilings = {};
|
|
18919
|
+
for (const model of modelInfo ?? []) {
|
|
18920
|
+
const ceiling = positiveInteger3(model.maxOutputTokens);
|
|
18921
|
+
if (ceiling !== undefined && used.has(model.modelID))
|
|
18922
|
+
ceilings[model.modelID] = ceiling;
|
|
18923
|
+
}
|
|
18924
|
+
return Object.keys(ceilings).length > 0 ? ceilings : undefined;
|
|
18925
|
+
}
|
|
18376
18926
|
function collectContextWindows(modelInfo, usedModelIDs) {
|
|
18377
18927
|
if (modelInfo === undefined) {
|
|
18378
18928
|
return;
|
|
@@ -18489,6 +19039,12 @@ var KNOWN_RUNTIMES = [
|
|
|
18489
19039
|
defaultBaseURL: LEMONADE_DEFAULT_BASE_URL,
|
|
18490
19040
|
fallbackModelID: "Qwen3-Coder-30B-A3B-Instruct-GGUF"
|
|
18491
19041
|
},
|
|
19042
|
+
{
|
|
19043
|
+
id: "llama-swap",
|
|
19044
|
+
label: executionSetupMessages.llamaSwap.label,
|
|
19045
|
+
defaultBaseURL: LLAMA_SWAP_DEFAULT_BASE_URL,
|
|
19046
|
+
fallbackModelID: ""
|
|
19047
|
+
},
|
|
18492
19048
|
{
|
|
18493
19049
|
id: "foundry-local",
|
|
18494
19050
|
label: "Foundry Local",
|
|
@@ -18496,7 +19052,7 @@ var KNOWN_RUNTIMES = [
|
|
|
18496
19052
|
dynamicPort: true
|
|
18497
19053
|
}
|
|
18498
19054
|
];
|
|
18499
|
-
var
|
|
19055
|
+
var RESIDENT_PROJECT_CONFIG_CANDIDATES = [
|
|
18500
19056
|
".opencode/opencode.jsonc",
|
|
18501
19057
|
".opencode/opencode.json",
|
|
18502
19058
|
"opencode.jsonc",
|
|
@@ -18518,6 +19074,19 @@ function choiceKind(choice) {
|
|
|
18518
19074
|
function choiceProviderID(choice) {
|
|
18519
19075
|
return choice.providerID ?? choice.id;
|
|
18520
19076
|
}
|
|
19077
|
+
function residencySetupMessages(kind) {
|
|
19078
|
+
return kind === "llama-swap" ? executionSetupMessages.llamaSwap : {
|
|
19079
|
+
noLoadedModels: executionSetupMessages.lemonadeNoLoadedModels,
|
|
19080
|
+
modelNotLoaded: executionSetupMessages.lemonadeModelNotLoaded,
|
|
19081
|
+
probeRequired: executionSetupMessages.lemonadeProbeRequired,
|
|
19082
|
+
probeFailed: executionSetupMessages.lemonadeProbeFailed,
|
|
19083
|
+
endpointMismatch: executionSetupMessages.lemonadeEndpointMismatch,
|
|
19084
|
+
modelIdentityUnknown: executionSetupMessages.lemonadeModelIdentityUnknown
|
|
19085
|
+
};
|
|
19086
|
+
}
|
|
19087
|
+
function residencyBaseURL(kind, value) {
|
|
19088
|
+
return kind === "llama-swap" ? llamaSwapBaseURL(value ?? LLAMA_SWAP_DEFAULT_BASE_URL) : lemonadeBaseURL(value ?? LEMONADE_DEFAULT_BASE_URL);
|
|
19089
|
+
}
|
|
18521
19090
|
function selectableModelIDs(models, modelInfo, kind) {
|
|
18522
19091
|
const byID = new Map(modelInfo?.map((model) => [model.modelID, model]));
|
|
18523
19092
|
return models.filter((id) => hasRequiredModelResidency(kind, byID.get(id)));
|
|
@@ -18535,7 +19104,7 @@ function assertRuntimeResidency(choice, primary) {
|
|
|
18535
19104
|
continue;
|
|
18536
19105
|
const info = choice.models.includes(id) ? choice.modelInfo?.find((model) => model.modelID === id) : undefined;
|
|
18537
19106
|
if (!hasRequiredModelResidency(kind, info))
|
|
18538
|
-
throw new Error(
|
|
19107
|
+
throw new Error(residencySetupMessages(kind).modelNotLoaded(`${choiceProviderID(choice)}/${id}`));
|
|
18539
19108
|
}
|
|
18540
19109
|
}
|
|
18541
19110
|
function assertSetupResidency(answers) {
|
|
@@ -18543,11 +19112,11 @@ function assertSetupResidency(answers) {
|
|
|
18543
19112
|
if (choice.enabled) {
|
|
18544
19113
|
assertRuntimeResidency(choice, answers.primaryModel);
|
|
18545
19114
|
} else if (choiceProviderID(choice) === answers.primaryModel.providerID && !hasRequiredModelResidency(localRuntimeKind(choice), undefined)) {
|
|
18546
|
-
throw new Error(
|
|
19115
|
+
throw new Error(residencySetupMessages(localRuntimeKind(choice)).modelNotLoaded(`${answers.primaryModel.providerID}/${answers.primaryModel.modelID}`));
|
|
18547
19116
|
}
|
|
18548
19117
|
}
|
|
18549
19118
|
}
|
|
18550
|
-
function
|
|
19119
|
+
function residentV2Policies(providers) {
|
|
18551
19120
|
return [...new Set(providers)].sort().map((providerID) => {
|
|
18552
19121
|
if (providerID.length === 0 || /[*?\\/\s]/.test(providerID))
|
|
18553
19122
|
throw new Error(executionSetupMessages.lemonadeV2InvalidProvider(providerID));
|
|
@@ -18564,7 +19133,7 @@ function buildOpenTeamConfig(answers) {
|
|
|
18564
19133
|
providerID: choiceProviderID(choice),
|
|
18565
19134
|
modelID: choice.defaultModelID
|
|
18566
19135
|
},
|
|
18567
|
-
maxConcurrency: choice
|
|
19136
|
+
maxConcurrency: effectiveMaxConcurrency(choice)
|
|
18568
19137
|
};
|
|
18569
19138
|
if (choice.modelConcurrency !== undefined && Object.keys(choice.modelConcurrency).length > 0) {
|
|
18570
19139
|
runtime.modelConcurrency = { ...choice.modelConcurrency };
|
|
@@ -18579,7 +19148,7 @@ function buildOpenTeamConfig(answers) {
|
|
|
18579
19148
|
runtime.kind = choice.kind;
|
|
18580
19149
|
}
|
|
18581
19150
|
if (choice.baseURL !== undefined) {
|
|
18582
|
-
runtime.baseURL = choice.baseURL;
|
|
19151
|
+
runtime.baseURL = localRuntimeKind(choice) === "llama-swap" ? llamaSwapBaseURL(choice.baseURL) : choice.baseURL;
|
|
18583
19152
|
} else if (knownRuntime(choiceKind(choice)).dynamicPort === true) {
|
|
18584
19153
|
runtime.discovery = "cli";
|
|
18585
19154
|
}
|
|
@@ -18625,7 +19194,7 @@ function orchestratorAgentModel(answers) {
|
|
|
18625
19194
|
}
|
|
18626
19195
|
function buildOpencodeConfig(answers) {
|
|
18627
19196
|
assertSetupResidency(answers);
|
|
18628
|
-
const policies =
|
|
19197
|
+
const policies = residentV2Policies(answers.runtimes.filter((choice) => choice.enabled && runtimeRequiresModelResidency(localRuntimeKind(choice))).map(choiceProviderID));
|
|
18629
19198
|
const provider = {};
|
|
18630
19199
|
const providerOwner = new Map;
|
|
18631
19200
|
for (const choice of answers.runtimes) {
|
|
@@ -18633,7 +19202,8 @@ function buildOpencodeConfig(answers) {
|
|
|
18633
19202
|
continue;
|
|
18634
19203
|
}
|
|
18635
19204
|
const meta = knownRuntime(choiceKind(choice));
|
|
18636
|
-
const
|
|
19205
|
+
const configuredBaseURL = choice.baseURL ?? meta.defaultBaseURL;
|
|
19206
|
+
const baseURL = meta.id === "llama-swap" ? llamaSwapBaseURL(configuredBaseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL) : configuredBaseURL;
|
|
18637
19207
|
if (baseURL === undefined) {
|
|
18638
19208
|
continue;
|
|
18639
19209
|
}
|
|
@@ -18652,7 +19222,7 @@ function buildOpencodeConfig(answers) {
|
|
|
18652
19222
|
const models = {};
|
|
18653
19223
|
for (const modelID of modelIDs) {
|
|
18654
19224
|
const contextWindow = choice.modelContextWindows?.[modelID];
|
|
18655
|
-
const limit = computeModelLimit(contextWindow);
|
|
19225
|
+
const limit = computeModelLimit(contextWindow, undefined, choice.modelMaxOutputTokens?.[modelID]);
|
|
18656
19226
|
models[modelID] = { name: modelID, limit };
|
|
18657
19227
|
}
|
|
18658
19228
|
const owner = providerOwner.get(providerID);
|
|
@@ -18833,8 +19403,8 @@ function preferredModelID(models, modelInfo, kind) {
|
|
|
18833
19403
|
async function chooseRuntimeModel(prompt, models, meta, modelInfo) {
|
|
18834
19404
|
const offered = selectableModelIDs(models, modelInfo, meta.id);
|
|
18835
19405
|
if (offered.length === 0) {
|
|
18836
|
-
if (meta.id
|
|
18837
|
-
throw new Error(
|
|
19406
|
+
if (runtimeRequiresModelResidency(meta.id))
|
|
19407
|
+
throw new Error(residencySetupMessages(meta.id).noLoadedModels);
|
|
18838
19408
|
return prompt.text({
|
|
18839
19409
|
message: `Default model for ${meta.label} (no models detected)`,
|
|
18840
19410
|
placeholder: meta.fallbackModelID,
|
|
@@ -18850,8 +19420,8 @@ async function chooseRuntimeModel(prompt, models, meta, modelInfo) {
|
|
|
18850
19420
|
opts.initial = first;
|
|
18851
19421
|
}
|
|
18852
19422
|
const selected = await prompt.select(opts);
|
|
18853
|
-
if (meta.id
|
|
18854
|
-
throw new Error(
|
|
19423
|
+
if (runtimeRequiresModelResidency(meta.id) && !offered.includes(selected))
|
|
19424
|
+
throw new Error(residencySetupMessages(meta.id).modelNotLoaded(selected));
|
|
18855
19425
|
return selected;
|
|
18856
19426
|
}
|
|
18857
19427
|
function remoteBaseURLExample(meta) {
|
|
@@ -18907,28 +19477,31 @@ async function resolveRuntimeConnection(deps, meta, info) {
|
|
|
18907
19477
|
initial: RUNTIME_LOCATION_LOCAL
|
|
18908
19478
|
});
|
|
18909
19479
|
if (location === RUNTIME_LOCATION_LOCAL) {
|
|
18910
|
-
if (meta.id
|
|
18911
|
-
throw new Error(
|
|
19480
|
+
if (runtimeRequiresModelResidency(meta.id) && info?.reachable !== true)
|
|
19481
|
+
throw new Error(info?.error ?? residencySetupMessages(meta.id).probeFailed);
|
|
18912
19482
|
const baseURL2 = info?.baseURL !== undefined && info.baseURL.length > 0 ? info.baseURL : meta.defaultBaseURL;
|
|
18913
|
-
return {
|
|
19483
|
+
return {
|
|
19484
|
+
baseURL: meta.id === "llama-swap" ? llamaSwapBaseURL(baseURL2) : baseURL2,
|
|
19485
|
+
...connectionOf(info)
|
|
19486
|
+
};
|
|
18914
19487
|
}
|
|
18915
19488
|
const entered = await deps.prompt.text({
|
|
18916
19489
|
message: `${meta.label} URL on the network (OpenAI-compatible endpoint)`,
|
|
18917
19490
|
placeholder: remoteBaseURLExample(meta)
|
|
18918
19491
|
});
|
|
18919
|
-
const baseURL = normalizeRemoteBaseURL(entered, meta.defaultBaseURL);
|
|
19492
|
+
const baseURL = meta.id === "llama-swap" ? llamaSwapBaseURL(entered) : normalizeRemoteBaseURL(entered, meta.defaultBaseURL);
|
|
18920
19493
|
let probedRuntime;
|
|
18921
|
-
if (meta.id
|
|
18922
|
-
throw new Error(
|
|
19494
|
+
if (runtimeRequiresModelResidency(meta.id) && deps.probeRuntime === undefined)
|
|
19495
|
+
throw new Error(residencySetupMessages(meta.id).probeRequired);
|
|
18923
19496
|
if (deps.probeRuntime !== undefined) {
|
|
18924
19497
|
try {
|
|
18925
19498
|
probedRuntime = await deps.probeRuntime(meta.id, baseURL);
|
|
18926
19499
|
} catch (cause) {
|
|
18927
|
-
if (meta.id
|
|
18928
|
-
throw new Error(
|
|
19500
|
+
if (runtimeRequiresModelResidency(meta.id))
|
|
19501
|
+
throw new Error(residencySetupMessages(meta.id).probeFailed, { cause });
|
|
18929
19502
|
}
|
|
18930
|
-
if (meta.id
|
|
18931
|
-
throw new Error(
|
|
19503
|
+
if (runtimeRequiresModelResidency(meta.id) && probedRuntime?.reachable !== true)
|
|
19504
|
+
throw new Error(probedRuntime?.error ?? residencySetupMessages(meta.id).probeFailed);
|
|
18932
19505
|
}
|
|
18933
19506
|
return { baseURL, ...connectionOf(probedRuntime) };
|
|
18934
19507
|
}
|
|
@@ -18936,8 +19509,8 @@ async function chooseClassModel(prompt, models, meta, defaultModelID, modelClass
|
|
|
18936
19509
|
const label = modelClass === "thinking" ? `Thinking model for ${meta.label} (design, analysis, review reasoning)` : `Coder model for ${meta.label} (writing and editing code)`;
|
|
18937
19510
|
const offered = selectableModelIDs(models, modelInfo, meta.id);
|
|
18938
19511
|
if (offered.length === 0) {
|
|
18939
|
-
if (meta.id
|
|
18940
|
-
throw new Error(
|
|
19512
|
+
if (runtimeRequiresModelResidency(meta.id))
|
|
19513
|
+
throw new Error(residencySetupMessages(meta.id).noLoadedModels);
|
|
18941
19514
|
const raw = await prompt.text({
|
|
18942
19515
|
message: `${label} — enter to reuse the default`,
|
|
18943
19516
|
placeholder: defaultModelID,
|
|
@@ -18953,8 +19526,8 @@ async function chooseClassModel(prompt, models, meta, defaultModelID, modelClass
|
|
|
18953
19526
|
})),
|
|
18954
19527
|
initial: defaultModelID
|
|
18955
19528
|
});
|
|
18956
|
-
if (meta.id
|
|
18957
|
-
throw new Error(
|
|
19529
|
+
if (runtimeRequiresModelResidency(meta.id) && !offered.includes(selected))
|
|
19530
|
+
throw new Error(residencySetupMessages(meta.id).modelNotLoaded(selected));
|
|
18958
19531
|
return selected;
|
|
18959
19532
|
}
|
|
18960
19533
|
async function resolveEnabledRuntime(deps, meta, info) {
|
|
@@ -18991,6 +19564,16 @@ async function resolveEnabledRuntime(deps, meta, info) {
|
|
|
18991
19564
|
if (modelContextWindows !== undefined) {
|
|
18992
19565
|
choice.modelContextWindows = modelContextWindows;
|
|
18993
19566
|
}
|
|
19567
|
+
const modelMaxOutputTokens = collectMaxOutputTokens(connection.modelInfo, [
|
|
19568
|
+
defaultModelID,
|
|
19569
|
+
thinkingModelID,
|
|
19570
|
+
coderModelID,
|
|
19571
|
+
...connection.models
|
|
19572
|
+
]);
|
|
19573
|
+
if (modelMaxOutputTokens !== undefined)
|
|
19574
|
+
choice.modelMaxOutputTokens = modelMaxOutputTokens;
|
|
19575
|
+
if (meta.id === "llama-swap")
|
|
19576
|
+
deps.prompt.note(executionSetupMessages.llamaSwap.declaredLimits, executionSetupMessages.llamaSwap.declaredLimitsTitle);
|
|
18994
19577
|
return choice;
|
|
18995
19578
|
}
|
|
18996
19579
|
function reportedModelSlots(modelInfo, usedModelIDs) {
|
|
@@ -19017,11 +19600,11 @@ async function promptSlotCount(prompt, meta, reported) {
|
|
|
19017
19600
|
}
|
|
19018
19601
|
const raw = await prompt.text({
|
|
19019
19602
|
message: `How many concurrent generation slots does ${meta.label} have? (match its real capacity; leaving one free keeps interactive work responsive)`,
|
|
19020
|
-
placeholder: String(
|
|
19021
|
-
initial: String(
|
|
19603
|
+
placeholder: String(effectiveMaxConcurrency({ id: meta.id })),
|
|
19604
|
+
initial: String(effectiveMaxConcurrency({ id: meta.id }))
|
|
19022
19605
|
});
|
|
19023
19606
|
const parsed = Number.parseInt(raw.trim(), 10);
|
|
19024
|
-
return Number.isInteger(parsed) && parsed > 0 ? parsed :
|
|
19607
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : effectiveMaxConcurrency({ id: meta.id });
|
|
19025
19608
|
}
|
|
19026
19609
|
function reportedSlotSummary(reported) {
|
|
19027
19610
|
const entries = Object.entries(reported ?? {});
|
|
@@ -19080,6 +19663,29 @@ async function resolveRuntimeInstances(deps, meta, info, usedIds) {
|
|
|
19080
19663
|
function isRecord6(value) {
|
|
19081
19664
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19082
19665
|
}
|
|
19666
|
+
function setupOutputCeilings(answers, existing) {
|
|
19667
|
+
const result = {};
|
|
19668
|
+
const providers = recordAt2(existing, "provider");
|
|
19669
|
+
for (const choice of answers.runtimes.filter((runtime) => runtime.enabled)) {
|
|
19670
|
+
const physical = choice.modelMaxOutputTokens;
|
|
19671
|
+
if (physical === undefined)
|
|
19672
|
+
continue;
|
|
19673
|
+
const providerID = choiceProviderID(choice);
|
|
19674
|
+
const ceilings = { ...physical };
|
|
19675
|
+
const models = recordAt2(recordAt2(providers, providerID), "models");
|
|
19676
|
+
for (const [alias, value] of Object.entries(models ?? {})) {
|
|
19677
|
+
if (!isRecord6(value))
|
|
19678
|
+
continue;
|
|
19679
|
+
const id = typeof value.id === "string" ? value.id : alias;
|
|
19680
|
+
delete ceilings[alias];
|
|
19681
|
+
const ceiling = physical[id];
|
|
19682
|
+
if (ceiling !== undefined)
|
|
19683
|
+
ceilings[alias] = ceiling;
|
|
19684
|
+
}
|
|
19685
|
+
result[providerID] = ceilings;
|
|
19686
|
+
}
|
|
19687
|
+
return result;
|
|
19688
|
+
}
|
|
19083
19689
|
var LIMIT_FIELDS = [
|
|
19084
19690
|
"context",
|
|
19085
19691
|
"input",
|
|
@@ -19569,7 +20175,7 @@ function readModelLimitValues(limit) {
|
|
|
19569
20175
|
function mergeModelLimitForRerun(input) {
|
|
19570
20176
|
const target = readModelLimitValues(input.targetLimit);
|
|
19571
20177
|
const effective = readModelLimitValues(input.effectiveLimit);
|
|
19572
|
-
const computed = computeModelLimit(effective.context ?? input.generatedLimit?.context, effective.input);
|
|
20178
|
+
const computed = computeModelLimit(effective.context ?? input.generatedLimit?.context, effective.input, input.maxOutputTokens);
|
|
19573
20179
|
const merged = { ...input.targetLimit ?? {} };
|
|
19574
20180
|
const targetOutputIsGeneratedDefault = target.output === DEFAULT_LOCAL_OUTPUT_TOKENS;
|
|
19575
20181
|
if (targetOutputIsGeneratedDefault && effective.output !== undefined && effective.output !== DEFAULT_LOCAL_OUTPUT_TOKENS) {
|
|
@@ -19583,6 +20189,8 @@ function mergeModelLimitForRerun(input) {
|
|
|
19583
20189
|
} else {
|
|
19584
20190
|
merged.output = computed.output;
|
|
19585
20191
|
}
|
|
20192
|
+
if (input.maxOutputTokens !== undefined && typeof merged.output === "number")
|
|
20193
|
+
merged.output = Math.min(merged.output, input.maxOutputTokens);
|
|
19586
20194
|
if (target.context !== undefined) {
|
|
19587
20195
|
merged.context = target.context;
|
|
19588
20196
|
} else if (effective.context !== undefined) {
|
|
@@ -19602,7 +20210,8 @@ function completePhysicalModelLimit(input) {
|
|
|
19602
20210
|
const target = readModelLimitValues(input.targetLimit);
|
|
19603
20211
|
const effective = readModelLimitValues(input.effectiveLimit);
|
|
19604
20212
|
const context = target.context ?? effective.context ?? input.generatedLimit?.context ?? 0;
|
|
19605
|
-
const
|
|
20213
|
+
const requestedOutput = target.output ?? effective.output ?? computeModelLimit(context, effective.input, input.maxOutputTokens).output;
|
|
20214
|
+
const output = input.maxOutputTokens === undefined ? requestedOutput : Math.min(requestedOutput, input.maxOutputTokens);
|
|
19606
20215
|
return { ...input.targetLimit, context, output };
|
|
19607
20216
|
}
|
|
19608
20217
|
function repairManagedModelLimits(input) {
|
|
@@ -19623,16 +20232,18 @@ function repairManagedModelLimits(input) {
|
|
|
19623
20232
|
let providerChanged = false;
|
|
19624
20233
|
const repairedModels = { ...targetModels };
|
|
19625
20234
|
for (const [modelID, modelValue] of Object.entries(targetModels)) {
|
|
19626
|
-
|
|
20235
|
+
const ceiling = input.modelOutputCeilings?.[providerID]?.[modelID];
|
|
20236
|
+
if (!isRecord6(modelValue) || !hasOwn2(modelValue, "limit") && ceiling === undefined) {
|
|
19627
20237
|
continue;
|
|
19628
20238
|
}
|
|
19629
|
-
const targetLimit = recordAt2(modelValue, "limit");
|
|
20239
|
+
const targetLimit = recordAt2(modelValue, "limit") ?? (ceiling === undefined ? undefined : {});
|
|
19630
20240
|
if (targetLimit === undefined) {
|
|
19631
20241
|
continue;
|
|
19632
20242
|
}
|
|
19633
20243
|
const effectiveLimit = recordAt2(recordAt2(effectiveModels, modelID), "limit");
|
|
19634
20244
|
const generatedLimit = generatedModelLimit(input.generated, providerID, modelID);
|
|
19635
20245
|
const repairedLimit = completePhysicalModelLimit({
|
|
20246
|
+
maxOutputTokens: ceiling,
|
|
19636
20247
|
...generatedLimit !== undefined ? { generatedLimit } : {},
|
|
19637
20248
|
targetLimit,
|
|
19638
20249
|
...effectiveLimit !== undefined ? { effectiveLimit } : {}
|
|
@@ -19691,7 +20302,7 @@ function highestExistingPinVersion(files) {
|
|
|
19691
20302
|
}
|
|
19692
20303
|
return highest;
|
|
19693
20304
|
}
|
|
19694
|
-
function
|
|
20305
|
+
function reconcileResidentV2Policies(raw, denials) {
|
|
19695
20306
|
if (denials.length === 0)
|
|
19696
20307
|
return raw;
|
|
19697
20308
|
const experimental = raw.experimental;
|
|
@@ -19750,6 +20361,7 @@ function mergeOpencodeConfigForRerun(generated, targetRaw, options) {
|
|
|
19750
20361
|
const targetLimit = recordAt2(targetModel, "limit");
|
|
19751
20362
|
const effectiveLimit = recordAt2(effectiveModel, "limit");
|
|
19752
20363
|
const mergedLimit = mergeModelLimitForRerun({
|
|
20364
|
+
maxOutputTokens: options.modelOutputCeilings?.[providerID]?.[modelID],
|
|
19753
20365
|
...modelConfig.limit !== undefined ? { generatedLimit: modelConfig.limit } : {},
|
|
19754
20366
|
...targetLimit !== undefined ? { targetLimit } : {},
|
|
19755
20367
|
...effectiveLimit !== undefined ? { effectiveLimit } : {}
|
|
@@ -19792,7 +20404,7 @@ function mergeOpencodeConfigForRerun(generated, targetRaw, options) {
|
|
|
19792
20404
|
if (managesV2Policies && hasOwn2(targetRaw, "experimental")) {
|
|
19793
20405
|
result.experimental = targetRaw.experimental;
|
|
19794
20406
|
}
|
|
19795
|
-
return
|
|
20407
|
+
return reconcileResidentV2Policies(result, generated.experimental?.policies ?? []);
|
|
19796
20408
|
}
|
|
19797
20409
|
function mergeOpenTeamConfigForRerun(generated, existing) {
|
|
19798
20410
|
const existingOrch = existing.orchestrator;
|
|
@@ -19842,9 +20454,9 @@ function mergeOpenTeamConfigForRerun(generated, existing) {
|
|
|
19842
20454
|
assertEffectiveConfigModelDomains(merged);
|
|
19843
20455
|
return merged;
|
|
19844
20456
|
}
|
|
19845
|
-
function
|
|
20457
|
+
function preserveRetainedResidentRuntimes(generated, previous, opencode) {
|
|
19846
20458
|
const providers = recordAt2(opencode, "provider") ?? {};
|
|
19847
|
-
const retained = (previous?.local.runtimes ?? []).filter((runtime) => localRuntimeKind(runtime)
|
|
20459
|
+
const retained = (previous?.local.runtimes ?? []).filter((runtime) => runtimeRequiresModelResidency(localRuntimeKind(runtime)) && hasOwn2(providers, runtime.defaultModel.providerID));
|
|
19848
20460
|
const deselected = retained.filter((runtime) => !generated.local.runtimes.some((selected) => selected.defaultModel.providerID === runtime.defaultModel.providerID)).map((runtime) => ({ ...runtime, enabled: false }));
|
|
19849
20461
|
return {
|
|
19850
20462
|
config: deselected.length === 0 ? generated : OpenTeamConfigSchema.parse({
|
|
@@ -19884,31 +20496,33 @@ async function confirmJsonNormalization(prompt, path2, format) {
|
|
|
19884
20496
|
initial: false
|
|
19885
20497
|
});
|
|
19886
20498
|
}
|
|
19887
|
-
function
|
|
19888
|
-
const
|
|
19889
|
-
if (
|
|
19890
|
-
throw new Error(
|
|
20499
|
+
function assertPreviousResidentPrimary(answers, previousConfig) {
|
|
20500
|
+
const priorResidentPrimary = previousConfig?.local.runtimes.find((runtime) => runtime.defaultModel.providerID === answers.primaryModel.providerID && runtimeRequiresModelResidency(localRuntimeKind(runtime)));
|
|
20501
|
+
if (priorResidentPrimary && !answers.runtimes.some((choice) => choice.enabled && choiceProviderID(choice) === answers.primaryModel.providerID)) {
|
|
20502
|
+
throw new Error(residencySetupMessages(localRuntimeKind(priorResidentPrimary)).modelNotLoaded(`${answers.primaryModel.providerID}/${answers.primaryModel.modelID}`));
|
|
19891
20503
|
}
|
|
19892
20504
|
}
|
|
19893
|
-
async function
|
|
20505
|
+
async function verifyResidencyBeforeWrite(deps, answers, config, opencode) {
|
|
19894
20506
|
for (const choice of answers.runtimes) {
|
|
19895
|
-
if (choice.enabled && localRuntimeKind(choice)
|
|
19896
|
-
throw new Error(
|
|
20507
|
+
if (choice.enabled && runtimeRequiresModelResidency(localRuntimeKind(choice)) && !config.local.runtimes.some((runtime) => runtime.enabled && runtime.defaultModel.providerID === choiceProviderID(choice) && localRuntimeKind(runtime) === localRuntimeKind(choice)))
|
|
20508
|
+
throw new Error(residencySetupMessages(localRuntimeKind(choice)).endpointMismatch);
|
|
19897
20509
|
}
|
|
19898
20510
|
for (const runtime of config.local.runtimes) {
|
|
19899
|
-
|
|
20511
|
+
const kind = localRuntimeKind(runtime);
|
|
20512
|
+
if (!runtimeRequiresModelResidency(kind))
|
|
19900
20513
|
continue;
|
|
20514
|
+
const messages = residencySetupMessages(kind);
|
|
19901
20515
|
const providerID = runtime.defaultModel.providerID;
|
|
19902
20516
|
const provider = isRecord6(opencode.provider) ? opencode.provider[providerID] : undefined;
|
|
19903
20517
|
if (!runtime.enabled && provider === undefined)
|
|
19904
20518
|
continue;
|
|
19905
20519
|
const endpoint = isRecord6(provider) ? (isRecord6(provider.options) ? provider.options.baseURL : undefined) ?? provider.api : undefined;
|
|
19906
|
-
if (typeof endpoint !== "string" ||
|
|
19907
|
-
throw new Error(
|
|
20520
|
+
if (typeof endpoint !== "string" || residencyBaseURL(kind, endpoint) !== residencyBaseURL(kind, runtime.baseURL) || config.local.runtimes.filter((candidate) => candidate.defaultModel.providerID === providerID).length !== 1)
|
|
20521
|
+
throw new Error(messages.endpointMismatch);
|
|
19908
20522
|
if (!runtime.enabled)
|
|
19909
20523
|
continue;
|
|
19910
20524
|
if (deps.probeRuntime === undefined)
|
|
19911
|
-
throw new Error(
|
|
20525
|
+
throw new Error(messages.probeRequired);
|
|
19912
20526
|
const references2 = new Set([
|
|
19913
20527
|
runtime.defaultModel.modelID,
|
|
19914
20528
|
runtime.thinkingModel,
|
|
@@ -19946,21 +20560,30 @@ async function verifyLemonadeBeforeWrite(deps, answers, config, opencode) {
|
|
|
19946
20560
|
const declaration = recordAt2(models, alias);
|
|
19947
20561
|
const wireID = declaration !== undefined && hasOwn2(declaration, "id") ? declaration.id : alias;
|
|
19948
20562
|
if (declaration === undefined || typeof wireID !== "string" || wireID.length === 0 || wireID !== wireID.trim())
|
|
19949
|
-
throw new Error(
|
|
20563
|
+
throw new Error(messages.modelIdentityUnknown(`${providerID}/${alias}`));
|
|
19950
20564
|
wireIDs.add(wireID);
|
|
19951
20565
|
}
|
|
19952
20566
|
let fresh;
|
|
19953
20567
|
try {
|
|
19954
20568
|
fresh = await deps.probeRuntime(localRuntimeKind(runtime), endpoint);
|
|
19955
20569
|
} catch (cause) {
|
|
19956
|
-
throw new Error(
|
|
20570
|
+
throw new Error(messages.probeFailed, { cause });
|
|
19957
20571
|
}
|
|
19958
|
-
if (!fresh.reachable || fresh.id !== localRuntimeKind(runtime) || fresh.baseURL
|
|
19959
|
-
throw new Error(
|
|
20572
|
+
if (!fresh.reachable || fresh.id !== localRuntimeKind(runtime) || residencyBaseURL(kind, fresh.baseURL) !== residencyBaseURL(kind, endpoint))
|
|
20573
|
+
throw new Error(fresh.error ?? messages.probeFailed);
|
|
19960
20574
|
for (const wireID of wireIDs) {
|
|
19961
20575
|
const info = fresh.models.includes(wireID) ? fresh.modelInfo?.find((model) => model.modelID === wireID) : undefined;
|
|
19962
|
-
if (!hasRequiredModelResidency(
|
|
19963
|
-
throw new Error(
|
|
20576
|
+
if (!hasRequiredModelResidency(kind, info))
|
|
20577
|
+
throw new Error(messages.modelNotLoaded(`${providerID}/${wireID}`));
|
|
20578
|
+
}
|
|
20579
|
+
for (const [alias, declaration] of Object.entries(models ?? {})) {
|
|
20580
|
+
if (!isRecord6(declaration))
|
|
20581
|
+
continue;
|
|
20582
|
+
const wireID = typeof declaration.id === "string" ? declaration.id : alias;
|
|
20583
|
+
const ceiling = fresh.modelInfo?.find((model) => model.modelID === wireID)?.maxOutputTokens;
|
|
20584
|
+
const output = positiveInteger3(recordAt2(declaration, "limit")?.output);
|
|
20585
|
+
if (ceiling !== undefined && output !== undefined && output > ceiling)
|
|
20586
|
+
throw new Error(executionSetupMessages.llamaSwap.outputChanged(`${providerID}/${alias}`, ceiling));
|
|
19964
20587
|
}
|
|
19965
20588
|
}
|
|
19966
20589
|
}
|
|
@@ -19975,7 +20598,7 @@ async function runSetup(deps) {
|
|
|
19975
20598
|
return {
|
|
19976
20599
|
value: meta.id,
|
|
19977
20600
|
label: meta.label,
|
|
19978
|
-
hint: info?.reachable ? meta.id
|
|
20601
|
+
hint: info?.reachable ? runtimeRequiresModelResidency(meta.id) ? executionSetupMessages.lemonadeDetectedHint(selectableModelIDs(info.models, info.modelInfo, meta.id).length, info.modelInfo?.length ?? info.models.length) : `detected · ${info.models.length} model(s)` : "not detected (you can point it to another machine)",
|
|
19979
20602
|
selected: info?.reachable ?? false
|
|
19980
20603
|
};
|
|
19981
20604
|
});
|
|
@@ -20092,7 +20715,7 @@ async function runSetup(deps) {
|
|
|
20092
20715
|
};
|
|
20093
20716
|
const openTeamConfig = buildOpenTeamConfig(answers);
|
|
20094
20717
|
const opencodeConfig = buildOpencodeConfig(answers);
|
|
20095
|
-
const opencodeConfigPaths =
|
|
20718
|
+
const opencodeConfigPaths = RESIDENT_PROJECT_CONFIG_CANDIDATES;
|
|
20096
20719
|
const orchestratorAgent = buildOrchestratorAgent(orchestratorAgentModel(answers), { yolo });
|
|
20097
20720
|
const slashCommands = buildOpenteamCommands();
|
|
20098
20721
|
const openTeamPath = DEFAULT_CONFIG_PATH;
|
|
@@ -20113,29 +20736,44 @@ async function runSetup(deps) {
|
|
|
20113
20736
|
}
|
|
20114
20737
|
throw error;
|
|
20115
20738
|
}
|
|
20116
|
-
|
|
20739
|
+
assertPreviousResidentPrimary(answers, existingOpenTeamConfig);
|
|
20117
20740
|
const effectiveExistingOpencode = mergeExistingOpencodeConfigFiles(existingOpencodeFiles, opencodeConfigPaths);
|
|
20741
|
+
const modelOutputCeilings = setupOutputCeilings(answers, effectiveExistingOpencode);
|
|
20742
|
+
const reductions = new Set;
|
|
20743
|
+
for (const file of existingOpencodeFiles) {
|
|
20744
|
+
for (const [providerID, ceilings] of Object.entries(modelOutputCeilings)) {
|
|
20745
|
+
const models = recordAt2(recordAt2(recordAt2(file.raw, "provider"), providerID), "models");
|
|
20746
|
+
for (const [modelID, ceiling] of Object.entries(ceilings)) {
|
|
20747
|
+
const output = positiveInteger3(recordAt2(recordAt2(models, modelID), "limit")?.output);
|
|
20748
|
+
if (output !== undefined && output > ceiling)
|
|
20749
|
+
reductions.add(executionSetupMessages.llamaSwap.outputReduction(`${providerID}/${modelID}`, output, ceiling));
|
|
20750
|
+
}
|
|
20751
|
+
}
|
|
20752
|
+
}
|
|
20753
|
+
if (reductions.size > 0)
|
|
20754
|
+
prompt.note([...reductions].join(`
|
|
20755
|
+
`), executionSetupMessages.llamaSwap.outputReductionTitle);
|
|
20118
20756
|
const {
|
|
20119
20757
|
config: finalOpenTeamConfig,
|
|
20120
|
-
providerIDs:
|
|
20121
|
-
} =
|
|
20758
|
+
providerIDs: retainedResidentProviderIDs
|
|
20759
|
+
} = preserveRetainedResidentRuntimes(existingOpenTeamConfig !== undefined ? mergeOpenTeamConfigForRerun(openTeamConfig, existingOpenTeamConfig) : openTeamConfig, existingOpenTeamConfig, effectiveExistingOpencode);
|
|
20122
20760
|
assertEffectiveConfigModelDomains(finalOpenTeamConfig);
|
|
20123
20761
|
const effectiveOpenTeamConfig = await effectiveSetupConfig(finalOpenTeamConfig, existingOpencodeFiles, deps);
|
|
20124
20762
|
assertEffectiveConfigModelDomains(effectiveOpenTeamConfig);
|
|
20125
|
-
for (const providerID of
|
|
20763
|
+
for (const providerID of retainedResidentProviderIDs) {
|
|
20126
20764
|
const matches = effectiveOpenTeamConfig.local.runtimes.filter((runtime) => runtime.defaultModel.providerID === providerID);
|
|
20127
|
-
if (matches.length !== 1 || matches[0] === undefined || localRuntimeKind(matches[0])
|
|
20128
|
-
throw new Error(executionSetupMessages.lemonadeRetainedProviderUnprotected(providerID));
|
|
20765
|
+
if (matches.length !== 1 || matches[0] === undefined || !runtimeRequiresModelResidency(localRuntimeKind(matches[0])))
|
|
20766
|
+
throw new Error(finalOpenTeamConfig.local.runtimes.some((runtime) => runtime.defaultModel.providerID === providerID && localRuntimeKind(runtime) === "lemonade") ? executionSetupMessages.lemonadeRetainedProviderUnprotected(providerID) : executionSetupMessages.retainedResidencyProviderUnprotected(providerID));
|
|
20129
20767
|
}
|
|
20130
|
-
const v2Denials =
|
|
20768
|
+
const v2Denials = residentV2Policies([
|
|
20131
20769
|
...opencodeConfig.experimental?.policies.map((policy) => policy.resource) ?? [],
|
|
20132
|
-
...effectiveOpenTeamConfig.local.runtimes.filter((runtime) => localRuntimeKind(runtime)
|
|
20770
|
+
...effectiveOpenTeamConfig.local.runtimes.filter((runtime) => runtimeRequiresModelResidency(localRuntimeKind(runtime))).map((runtime) => runtime.defaultModel.providerID)
|
|
20133
20771
|
]);
|
|
20134
20772
|
if (v2Denials.length > 0)
|
|
20135
20773
|
opencodeConfig.experimental = { policies: v2Denials };
|
|
20136
|
-
const v2Notice = v2Denials.length > 0 ? executionSetupMessages.
|
|
20774
|
+
const v2Notice = v2Denials.length > 0 ? executionSetupMessages.residencyV2Restriction(v2Denials.map((policy) => policy.resource)) : undefined;
|
|
20137
20775
|
if (v2Notice !== undefined)
|
|
20138
|
-
prompt.note(v2Notice, executionSetupMessages.
|
|
20776
|
+
prompt.note(v2Notice, executionSetupMessages.residencyV2Title);
|
|
20139
20777
|
const relevantProviderIDs = relevantLocalProviderIDs(opencodeConfig, existingOpenTeamConfig);
|
|
20140
20778
|
for (const file of existingOpencodeFiles) {
|
|
20141
20779
|
assertRelevantLimitNumbers(file, relevantProviderIDs);
|
|
@@ -20166,12 +20804,14 @@ async function runSetup(deps) {
|
|
|
20166
20804
|
const includePluginInDataFile = pluginOwnerPath === opencodeDataPath;
|
|
20167
20805
|
const dataTargetRaw = opencodeFileAt(existingOpencodeFiles, opencodeDataPath)?.raw ?? {};
|
|
20168
20806
|
const mergedOpencodeRaw = mergeOpencodeConfigForRerun(opencodeConfig, dataTargetRaw, {
|
|
20807
|
+
modelOutputCeilings,
|
|
20169
20808
|
...effectiveExistingOpencode !== undefined ? { effectiveRaw: effectiveExistingOpencode } : {},
|
|
20170
20809
|
includePlugin: includePluginInDataFile,
|
|
20171
20810
|
...includePluginInDataFile ? { pluginEntries: pluginPlan.plugins } : {},
|
|
20172
20811
|
yolo
|
|
20173
20812
|
});
|
|
20174
20813
|
const repairedDataTarget = repairManagedModelLimits({
|
|
20814
|
+
modelOutputCeilings,
|
|
20175
20815
|
raw: mergedOpencodeRaw,
|
|
20176
20816
|
effectiveRaw: effectiveExistingOpencode,
|
|
20177
20817
|
generated: opencodeConfig,
|
|
@@ -20185,12 +20825,13 @@ async function runSetup(deps) {
|
|
|
20185
20825
|
continue;
|
|
20186
20826
|
}
|
|
20187
20827
|
const repaired = repairManagedModelLimits({
|
|
20828
|
+
modelOutputCeilings,
|
|
20188
20829
|
raw: file.raw,
|
|
20189
20830
|
effectiveRaw: effectiveExistingOpencode,
|
|
20190
20831
|
generated: opencodeConfig,
|
|
20191
20832
|
providerIDs: relevantProviderIDs
|
|
20192
20833
|
});
|
|
20193
|
-
let plannedRaw =
|
|
20834
|
+
let plannedRaw = reconcileResidentV2Policies(repaired.raw, v2Denials);
|
|
20194
20835
|
let changed = repaired.changed || plannedRaw !== repaired.raw;
|
|
20195
20836
|
if (file.path === pluginOwnerPath && !includePluginInDataFile && pluginPlan.changed) {
|
|
20196
20837
|
plannedRaw = { ...plannedRaw, plugin: [...pluginPlan.plugins] };
|
|
@@ -20264,7 +20905,7 @@ async function runSetup(deps) {
|
|
|
20264
20905
|
const effectivePlannedOpencode = mergeExistingOpencodeConfigFiles(existingOpencodeFiles, opencodeConfigPaths, opencodeWritePlan);
|
|
20265
20906
|
if (effectivePlannedOpencode === undefined)
|
|
20266
20907
|
throw new Error(executionSetupMessages.lemonadeEndpointMismatch);
|
|
20267
|
-
await
|
|
20908
|
+
await verifyResidencyBeforeWrite(deps, answers, effectiveOpenTeamConfig, effectivePlannedOpencode);
|
|
20268
20909
|
await deps.writeFile(openTeamPath, serializeOpenTeamConfig(finalOpenTeamConfig));
|
|
20269
20910
|
for (const [path2, contents] of opencodeWritePlan) {
|
|
20270
20911
|
await deps.writeFile(path2, serializeOpencodeConfig(contents));
|
|
@@ -22647,7 +23288,7 @@ function liveModelsFromSnapshots(snapshots, config) {
|
|
|
22647
23288
|
map.set(runtime.defaultModel.providerID, {
|
|
22648
23289
|
reachable: snapshot.reachable,
|
|
22649
23290
|
modelIDs: new Set(snapshot.models.filter((model) => hasRequiredModelResidency(kind, model)).map((model) => model.modelID)),
|
|
22650
|
-
...kind
|
|
23291
|
+
...runtimeRequiresModelResidency(kind) ? {
|
|
22651
23292
|
residency: new Map(snapshot.models.map((model) => [model.modelID, model.loaded]))
|
|
22652
23293
|
} : {}
|
|
22653
23294
|
});
|
|
@@ -22977,9 +23618,9 @@ async function runCli(argv, deps) {
|
|
|
22977
23618
|
const inventoryNotes = [];
|
|
22978
23619
|
const readySnapshots = snapshots.map((snapshot) => {
|
|
22979
23620
|
const runtime = config.local.runtimes.find((runtime2) => runtime2.id === snapshot.id);
|
|
22980
|
-
if (runtime === undefined || localRuntimeKind(runtime)
|
|
23621
|
+
if (runtime === undefined || !runtimeRequiresModelResidency(localRuntimeKind(runtime)))
|
|
22981
23622
|
return snapshot;
|
|
22982
|
-
inventoryNotes.push(executionSetupMessages.lemonadeInventory(runtime.id, runtime.defaultModel.providerID, snapshot.models));
|
|
23623
|
+
inventoryNotes.push(executionSetupMessages.lemonadeInventory(runtime.id, runtime.defaultModel.providerID, snapshot.models, localRuntimeKind(runtime) === "llama-swap" ? "llama-swap" : "Lemonade"));
|
|
22983
23624
|
return {
|
|
22984
23625
|
...snapshot,
|
|
22985
23626
|
models: snapshot.models.filter((model) => hasRequiredModelResidency(localRuntimeKind(runtime), model))
|
|
@@ -22990,7 +23631,7 @@ async function runCli(argv, deps) {
|
|
|
22990
23631
|
if (agent.model === undefined || live !== "not-loaded" && live !== "residency-unknown")
|
|
22991
23632
|
return [];
|
|
22992
23633
|
return [
|
|
22993
|
-
executionSetupMessages.lemonadeAgentResidency(agent.name, `${agent.model.providerID}/${agent.model.modelID}`, live)
|
|
23634
|
+
executionSetupMessages.lemonadeAgentResidency(agent.name, `${agent.model.providerID}/${agent.model.modelID}`, live, config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === agent.model?.providerID && localRuntimeKind(runtime) === "llama-swap") ? "llama-swap" : "Lemonade")
|
|
22994
23635
|
];
|
|
22995
23636
|
});
|
|
22996
23637
|
return {
|
|
@@ -24812,6 +25453,159 @@ var realCacheAdapter = {
|
|
|
24812
25453
|
isReparsePath
|
|
24813
25454
|
};
|
|
24814
25455
|
|
|
25456
|
+
// src/local/lemonadeRequestGuard.ts
|
|
25457
|
+
var inferencePaths = new Set([
|
|
25458
|
+
"/chat/completions",
|
|
25459
|
+
"/completions",
|
|
25460
|
+
"/embeddings",
|
|
25461
|
+
"/responses"
|
|
25462
|
+
]);
|
|
25463
|
+
function modelID(payload) {
|
|
25464
|
+
if (typeof payload !== "object" || payload === null || !("model" in payload) || typeof payload.model !== "string" || payload.model.length === 0 || payload.model !== payload.model.trim()) {
|
|
25465
|
+
throw new ModelResidencyError("request");
|
|
25466
|
+
}
|
|
25467
|
+
if (payload.model.endsWith(":latest"))
|
|
25468
|
+
throw new ModelResidencyError("identity");
|
|
25469
|
+
return payload.model;
|
|
25470
|
+
}
|
|
25471
|
+
function createLemonadeGuardedFetch(options) {
|
|
25472
|
+
const base = lemonadeBaseURL(options.baseURL);
|
|
25473
|
+
const upstream = options.fetch;
|
|
25474
|
+
const healthFetch = options.healthFetch ?? upstream;
|
|
25475
|
+
return Object.assign(async (input, init) => {
|
|
25476
|
+
const request = new Request(input, init);
|
|
25477
|
+
const url = new URL(request.url);
|
|
25478
|
+
if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !request.url.startsWith(`${base}/`)) {
|
|
25479
|
+
throw new ModelResidencyError("destination");
|
|
25480
|
+
}
|
|
25481
|
+
const path4 = request.url.slice(base.length);
|
|
25482
|
+
const metadataOnly = request.method === "GET" && (path4 === "/health" || /^\/models(?:\/[^/]+)?$/.test(path4));
|
|
25483
|
+
if (!metadataOnly && (request.method !== "POST" || !inferencePaths.has(path4))) {
|
|
25484
|
+
throw new ModelResidencyError("request");
|
|
25485
|
+
}
|
|
25486
|
+
if (!metadataOnly) {
|
|
25487
|
+
await withResidencyDeadline(async (signal) => {
|
|
25488
|
+
signal.throwIfAborted();
|
|
25489
|
+
let payload;
|
|
25490
|
+
try {
|
|
25491
|
+
payload = await request.clone().json();
|
|
25492
|
+
} catch {
|
|
25493
|
+
signal.throwIfAborted();
|
|
25494
|
+
throw new ModelResidencyError("request");
|
|
25495
|
+
}
|
|
25496
|
+
signal.throwIfAborted();
|
|
25497
|
+
const model = modelID(payload);
|
|
25498
|
+
const modelIDs = typeof options.modelIDs === "function" ? await options.modelIDs() : options.modelIDs;
|
|
25499
|
+
signal.throwIfAborted();
|
|
25500
|
+
if (modelIDs !== undefined && !modelIDs.has(model)) {
|
|
25501
|
+
throw new ModelResidencyError("identity");
|
|
25502
|
+
}
|
|
25503
|
+
const residency = await readLemonadeResidency(base, healthFetch, {
|
|
25504
|
+
...options,
|
|
25505
|
+
signal,
|
|
25506
|
+
headers: request.headers
|
|
25507
|
+
});
|
|
25508
|
+
signal.throwIfAborted();
|
|
25509
|
+
const entry = residency.get(model);
|
|
25510
|
+
if (entry?.loaded !== true) {
|
|
25511
|
+
throw new ModelResidencyError(entry?.loaded === undefined && entry !== undefined ? "unknown" : "inactive");
|
|
25512
|
+
}
|
|
25513
|
+
}, { ...options, signal: request.signal });
|
|
25514
|
+
}
|
|
25515
|
+
if (request.signal.aborted)
|
|
25516
|
+
throw new ModelResidencyError("cancelled");
|
|
25517
|
+
const {
|
|
25518
|
+
body: _body,
|
|
25519
|
+
headers: _headers,
|
|
25520
|
+
method: _method,
|
|
25521
|
+
signal: _signal,
|
|
25522
|
+
redirect: _redirect,
|
|
25523
|
+
...transportOptions
|
|
25524
|
+
} = init ?? {};
|
|
25525
|
+
return upstream(request, { ...transportOptions, redirect: "error" });
|
|
25526
|
+
}, {
|
|
25527
|
+
preconnect: (...args) => upstream.preconnect?.(...args)
|
|
25528
|
+
});
|
|
25529
|
+
}
|
|
25530
|
+
|
|
25531
|
+
// src/local/llamaSwapRequestGuard.ts
|
|
25532
|
+
var inferencePaths2 = new Set([
|
|
25533
|
+
"/chat/completions",
|
|
25534
|
+
"/completions",
|
|
25535
|
+
"/embeddings",
|
|
25536
|
+
"/responses"
|
|
25537
|
+
]);
|
|
25538
|
+
async function validatedModelID(payload, modelIDs) {
|
|
25539
|
+
if (typeof payload !== "object" || payload === null || !("model" in payload) || typeof payload.model !== "string" || payload.model.length === 0 || payload.model !== payload.model.trim()) {
|
|
25540
|
+
throw new ModelResidencyError("request", "llama-swap");
|
|
25541
|
+
}
|
|
25542
|
+
if (modelIDs === undefined) {
|
|
25543
|
+
return payload.model;
|
|
25544
|
+
}
|
|
25545
|
+
const ids = typeof modelIDs === "function" ? await modelIDs() : modelIDs;
|
|
25546
|
+
if (ids.has(payload.model)) {
|
|
25547
|
+
return payload.model;
|
|
25548
|
+
}
|
|
25549
|
+
throw new ModelResidencyError("identity", "llama-swap");
|
|
25550
|
+
}
|
|
25551
|
+
function createLlamaSwapGuardedFetch(options) {
|
|
25552
|
+
const base = llamaSwapBaseURL(options.baseURL);
|
|
25553
|
+
const upstream = options.fetch;
|
|
25554
|
+
const healthFetch = options.healthFetch ?? upstream;
|
|
25555
|
+
return Object.assign(async (input, init) => {
|
|
25556
|
+
const request = new Request(input, init);
|
|
25557
|
+
const url = new URL(request.url);
|
|
25558
|
+
if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !request.url.startsWith(`${base}/`)) {
|
|
25559
|
+
throw new ModelResidencyError("destination", "llama-swap");
|
|
25560
|
+
}
|
|
25561
|
+
const path4 = request.url.slice(base.length);
|
|
25562
|
+
const metadataOnly = request.method === "GET" && /^\/models(?:\/[^/]+)?$/.test(path4);
|
|
25563
|
+
if (!metadataOnly && (request.method !== "POST" || !inferencePaths2.has(path4))) {
|
|
25564
|
+
throw new ModelResidencyError("request", "llama-swap");
|
|
25565
|
+
}
|
|
25566
|
+
if (!metadataOnly) {
|
|
25567
|
+
await withResidencyDeadline(async (signal) => {
|
|
25568
|
+
signal.throwIfAborted();
|
|
25569
|
+
let payload;
|
|
25570
|
+
try {
|
|
25571
|
+
payload = await request.clone().json();
|
|
25572
|
+
} catch {
|
|
25573
|
+
signal.throwIfAborted();
|
|
25574
|
+
throw new ModelResidencyError("request", "llama-swap");
|
|
25575
|
+
}
|
|
25576
|
+
signal.throwIfAborted();
|
|
25577
|
+
const model = await validatedModelID(payload, options.modelIDs);
|
|
25578
|
+
const residency = await readLlamaSwapResidency(base, healthFetch, {
|
|
25579
|
+
...options,
|
|
25580
|
+
signal,
|
|
25581
|
+
headers: request.headers
|
|
25582
|
+
});
|
|
25583
|
+
signal.throwIfAborted();
|
|
25584
|
+
if (!residency.has(model)) {
|
|
25585
|
+
throw new ModelResidencyError("identity", "llama-swap");
|
|
25586
|
+
}
|
|
25587
|
+
if (residency.get(model)?.loaded !== true) {
|
|
25588
|
+
throw new ModelResidencyError("inactive", "llama-swap");
|
|
25589
|
+
}
|
|
25590
|
+
}, { ...options, signal: request.signal, runtimeLabel: "llama-swap" });
|
|
25591
|
+
}
|
|
25592
|
+
if (request.signal.aborted) {
|
|
25593
|
+
throw new ModelResidencyError("cancelled", "llama-swap");
|
|
25594
|
+
}
|
|
25595
|
+
const {
|
|
25596
|
+
body: _body,
|
|
25597
|
+
headers: _headers,
|
|
25598
|
+
method: _method,
|
|
25599
|
+
signal: _signal,
|
|
25600
|
+
redirect: _redirect,
|
|
25601
|
+
...transportOptions
|
|
25602
|
+
} = init ?? {};
|
|
25603
|
+
return upstream(request, { ...transportOptions, redirect: "error" });
|
|
25604
|
+
}, {
|
|
25605
|
+
preconnect: (...args) => upstream.preconnect?.(...args)
|
|
25606
|
+
});
|
|
25607
|
+
}
|
|
25608
|
+
|
|
24815
25609
|
// src/memory/types.ts
|
|
24816
25610
|
import { z as z28 } from "zod";
|
|
24817
25611
|
var SHARED_OWNER_KEY = "*";
|
|
@@ -25476,6 +26270,13 @@ function createLocalEmbedder(request, deps) {
|
|
|
25476
26270
|
return (texts) => embedTexts({ ...request, texts }, deps);
|
|
25477
26271
|
}
|
|
25478
26272
|
|
|
26273
|
+
// src/messages/memoryRuntime.ts
|
|
26274
|
+
var memoryRuntimeMessages = {
|
|
26275
|
+
providerUnavailable: "[openteam] Semantic memory requires exactly one enabled local runtime matching the configured embeddings provider. No embedding request was sent.",
|
|
26276
|
+
unavailable: "[openteam] Semantic memory initialization failed; recall and memory injection are unavailable.",
|
|
26277
|
+
rejectedLines: (count) => `[openteam] memory log rejected ${count} line(s); semantic memory may be incomplete.`
|
|
26278
|
+
};
|
|
26279
|
+
|
|
25479
26280
|
// src/memory/embedText.ts
|
|
25480
26281
|
function normalize3(parts) {
|
|
25481
26282
|
return parts.map((part) => part.trim()).filter((part) => part.length > 0).join(" ");
|
|
@@ -25553,25 +26354,39 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
|
|
|
25553
26354
|
if (options.requireInjectionEnabled === true && !semantic.injection.enabled) {
|
|
25554
26355
|
return;
|
|
25555
26356
|
}
|
|
25556
|
-
const
|
|
26357
|
+
const warn = deps.warn ?? console.warn;
|
|
26358
|
+
const configuredModel = semantic.embeddings.model;
|
|
26359
|
+
const candidates = configuredModel == null ? config.local.runtimes.filter((rt) => rt.enabled && rt.baseURL !== undefined) : config.local.runtimes.filter((rt) => rt.defaultModel.providerID === configuredModel.providerID);
|
|
26360
|
+
const runtime = candidates[0];
|
|
26361
|
+
if (configuredModel != null && (candidates.length !== 1 || runtime?.enabled !== true)) {
|
|
26362
|
+
warn(memoryRuntimeMessages.providerUnavailable);
|
|
26363
|
+
return;
|
|
26364
|
+
}
|
|
25557
26365
|
if (runtime?.baseURL === undefined) {
|
|
25558
26366
|
return;
|
|
25559
26367
|
}
|
|
25560
|
-
const
|
|
26368
|
+
const modelID2 = semantic.embeddings.model?.modelID ?? runtime.defaultModel.modelID;
|
|
25561
26369
|
const openDatabase = deps.openDatabase ?? createBunSqliteDatabase;
|
|
25562
26370
|
const makeEmbedder = deps.createEmbedder ?? createLocalEmbedder;
|
|
25563
26371
|
const fetchImpl = deps.fetch ?? globalThis.fetch;
|
|
25564
26372
|
const now = deps.now ?? (() => Date.now());
|
|
25565
|
-
const
|
|
26373
|
+
const kind = localRuntimeKind(runtime);
|
|
25566
26374
|
try {
|
|
26375
|
+
const baseURL = kind === "llama-swap" ? llamaSwapBaseURL(runtime.baseURL) : runtime.baseURL;
|
|
26376
|
+
const guardedFetch = runtimeRequiresModelResidency(kind) ? (kind === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch)({
|
|
26377
|
+
baseURL,
|
|
26378
|
+
fetch: fetchImpl,
|
|
26379
|
+
healthFetch: fetchImpl,
|
|
26380
|
+
modelIDs: new Set([modelID2])
|
|
26381
|
+
}) : fetchImpl;
|
|
25567
26382
|
const db = await openDatabase(semantic.indexPath);
|
|
25568
26383
|
const index = createMemoryIndex(db, warn);
|
|
25569
26384
|
const rebuildResult = await rebuildMemoryIndexFromStorage(index, semantic.logPath, { storage: deps.storage });
|
|
25570
26385
|
if (rebuildResult.rejectedLines > 0) {
|
|
25571
|
-
warn(
|
|
26386
|
+
warn(memoryRuntimeMessages.rejectedLines(rebuildResult.rejectedLines));
|
|
25572
26387
|
}
|
|
25573
|
-
const embedder = makeEmbedder({ baseURL
|
|
25574
|
-
await ensureMemoryEmbeddings({ index, embedder, model:
|
|
26388
|
+
const embedder = makeEmbedder({ baseURL, modelID: modelID2 }, { fetch: guardedFetch });
|
|
26389
|
+
await ensureMemoryEmbeddings({ index, embedder, model: modelID2, warn });
|
|
25575
26390
|
return {
|
|
25576
26391
|
index,
|
|
25577
26392
|
embedder,
|
|
@@ -25580,6 +26395,7 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
|
|
|
25580
26395
|
semantic
|
|
25581
26396
|
};
|
|
25582
26397
|
} catch {
|
|
26398
|
+
warn(memoryRuntimeMessages.unavailable);
|
|
25583
26399
|
return;
|
|
25584
26400
|
}
|
|
25585
26401
|
}
|