@jmanuelcorral/openteam 0.25.0 → 0.27.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 +39 -13
- package/README.md +39 -14
- package/dist/certificates/graph-release-certificate.json +2 -2
- package/dist/certificates/graph-shadow-certificate.json +2 -2
- package/dist/certificates/opencode-2.0.5/graph-release-certificate.json +45 -0
- package/dist/certificates/opencode-2.0.5/graph-shadow-certificate.json +39 -0
- package/dist/cli/consoleRuntimeState.d.ts.map +1 -1
- package/dist/cli/consoleServe.d.ts +2 -1
- package/dist/cli/consoleServe.d.ts.map +1 -1
- package/dist/cli/setupAdapters.d.ts.map +1 -1
- package/dist/cli.js +24534 -14551
- 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 +22 -15
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/upgrade.d.ts +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/config/fieldProjection.d.ts +3 -0
- package/dist/config/fieldProjection.d.ts.map +1 -0
- package/dist/config/nativeOpencode.d.ts +5 -0
- package/dist/config/nativeOpencode.d.ts.map +1 -0
- package/dist/config/schema.d.ts +101 -7
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/console/auth.d.ts +5 -0
- package/dist/console/auth.d.ts.map +1 -0
- package/dist/console/conversation.d.ts +8 -1
- package/dist/console/conversation.d.ts.map +1 -1
- package/dist/console/historyBoundary.d.ts +1 -1
- package/dist/console/historyBoundary.d.ts.map +1 -1
- package/dist/console/nativeConversation.d.ts +7 -0
- package/dist/console/nativeConversation.d.ts.map +1 -0
- package/dist/console/opencodeClient.d.ts +3 -0
- package/dist/console/opencodeClient.d.ts.map +1 -1
- package/dist/console/pty.d.ts +4 -0
- package/dist/console/pty.d.ts.map +1 -1
- package/dist/contract/opencode.d.ts +13 -7
- package/dist/contract/opencode.d.ts.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1796 -327
- 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 +3 -0
- 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/messages/nativeHost.d.ts +21 -0
- package/dist/messages/nativeHost.d.ts.map +1 -0
- package/dist/plugin/availability.d.ts +1 -0
- package/dist/plugin/availability.d.ts.map +1 -1
- package/dist/plugin/consoleTool.d.ts +1 -0
- package/dist/plugin/consoleTool.d.ts.map +1 -1
- package/dist/plugin/hooks.d.ts +3 -3
- package/dist/plugin/hooks.d.ts.map +1 -1
- package/dist/plugin/memoryTool.d.ts.map +1 -1
- package/dist/plugin/modelResidency.d.ts +1 -0
- package/dist/plugin/modelResidency.d.ts.map +1 -1
- package/dist/plugin/nativeHost.d.ts +5 -0
- package/dist/plugin/nativeHost.d.ts.map +1 -0
- package/dist/plugin/nativeHostContext.d.ts +228 -0
- package/dist/plugin/nativeHostContext.d.ts.map +1 -0
- package/dist/plugin/nativeInventory.d.ts +5 -0
- package/dist/plugin/nativeInventory.d.ts.map +1 -0
- package/dist/plugin/nativeResidency.d.ts +30 -0
- package/dist/plugin/nativeResidency.d.ts.map +1 -0
- package/dist/plugin/nativeSessions.d.ts +131 -0
- package/dist/plugin/nativeSessions.d.ts.map +1 -0
- package/dist/plugin/orchestrateTool.d.ts.map +1 -1
- package/dist/plugin/reloadTool.d.ts +3 -2
- package/dist/plugin/reloadTool.d.ts.map +1 -1
- package/dist/plugin/runtimeContext.d.ts +42 -0
- package/dist/plugin/runtimeContext.d.ts.map +1 -0
- package/dist/storage/index/memoryRuntime.d.ts.map +1 -1
- package/dist/web/console.d.ts +1 -1
- package/dist/web/console.d.ts.map +1 -1
- package/dist/web/server.d.ts +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/start.d.ts +1 -1
- package/dist/web/start.d.ts.map +1 -1
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -784,6 +784,9 @@ function migrateConfig(raw) {
|
|
|
784
784
|
|
|
785
785
|
// src/messages/consoleDetail.ts
|
|
786
786
|
var consoleDetailMessages = {
|
|
787
|
+
runtimeStateCleanupFailed: "The Console runtime state could not be published and its staging file could not be removed.",
|
|
788
|
+
requestFailed: "The local opencode request failed.",
|
|
789
|
+
nativeToolFailed: "The native tool execution failed.",
|
|
787
790
|
follow: "Follow new activity",
|
|
788
791
|
metadataOnly: "Lifecycle and telemetry stay metadata-only.",
|
|
789
792
|
logsDescription: "Current recorded conversations load automatically. Lifecycle and telemetry remain metadata-only; credentials are masked.",
|
|
@@ -1049,7 +1052,8 @@ var LocalRuntimeKindSchema = z3.enum([
|
|
|
1049
1052
|
"ollama",
|
|
1050
1053
|
"lmstudio",
|
|
1051
1054
|
"foundry-local",
|
|
1052
|
-
"lemonade"
|
|
1055
|
+
"lemonade",
|
|
1056
|
+
"llama-swap"
|
|
1053
1057
|
]);
|
|
1054
1058
|
var LocalRuntimeSchema = z3.object({
|
|
1055
1059
|
id: z3.string().min(1),
|
|
@@ -1069,15 +1073,25 @@ var LocalRuntimeSchema = z3.object({
|
|
|
1069
1073
|
ctx.addIssue({
|
|
1070
1074
|
code: z3.ZodIssueCode.custom,
|
|
1071
1075
|
path: ["kind"],
|
|
1072
|
-
message: `local runtime "${runtime.id}" has no resolvable kind: add "kind" ` + "(one of ollama, lmstudio, foundry-local, lemonade), since the id is
|
|
1076
|
+
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."
|
|
1073
1077
|
});
|
|
1074
1078
|
}
|
|
1079
|
+
}).transform((runtime) => {
|
|
1080
|
+
const resolvedKind = runtime.kind ?? runtime.id;
|
|
1081
|
+
if (resolvedKind !== "llama-swap" || runtime.maxConcurrency !== undefined) {
|
|
1082
|
+
return runtime;
|
|
1083
|
+
}
|
|
1084
|
+
return { ...runtime, maxConcurrency: 1 };
|
|
1075
1085
|
});
|
|
1076
1086
|
function localRuntimeKind(runtime) {
|
|
1077
1087
|
return runtime.kind ?? runtime.id;
|
|
1078
1088
|
}
|
|
1079
1089
|
function effectiveMaxConcurrency(runtime) {
|
|
1080
|
-
|
|
1090
|
+
if (runtime.maxConcurrency !== undefined) {
|
|
1091
|
+
return runtime.maxConcurrency;
|
|
1092
|
+
}
|
|
1093
|
+
const kind = runtime.kind ?? (runtime.id !== undefined && LocalRuntimeKindSchema.safeParse(runtime.id).success ? runtime.id : undefined);
|
|
1094
|
+
return kind === "llama-swap" ? 1 : DEFAULT_LOCAL_MAX_CONCURRENCY;
|
|
1081
1095
|
}
|
|
1082
1096
|
var MemoryScopeSchema = z3.enum(["project", "user"]);
|
|
1083
1097
|
var MemoryExtractionModeSchema = z3.enum([
|
|
@@ -5746,7 +5760,7 @@ import { z as z15 } from "zod";
|
|
|
5746
5760
|
// package.json
|
|
5747
5761
|
var package_default = {
|
|
5748
5762
|
name: "@jmanuelcorral/openteam",
|
|
5749
|
-
version: "0.
|
|
5763
|
+
version: "0.27.0",
|
|
5750
5764
|
packageManager: "bun@1.3.14",
|
|
5751
5765
|
description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
|
|
5752
5766
|
license: "MIT",
|
|
@@ -5806,12 +5820,13 @@ var package_default = {
|
|
|
5806
5820
|
scripts: {
|
|
5807
5821
|
prebuild: "bun run clean",
|
|
5808
5822
|
build: "bun run build:js && bun run build:cli && bun run build:types && bun run build:certificates",
|
|
5809
|
-
"build:js": "bun build ./src/index.ts --target=node --format=esm --outfile=dist/index.js --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph",
|
|
5810
|
-
"build:cli": 'bun build ./src/cli.ts --target=node --format=esm --outfile=dist/cli.js --banner "#!/usr/bin/env node" --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @clack/prompts --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph',
|
|
5823
|
+
"build:js": "bun build ./src/index.ts --target=node --format=esm --outfile=dist/index.js --external @opencode-ai/plugin --external @opencode-ai/sdk --external @opencode/schema --external effect --external zod --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph",
|
|
5824
|
+
"build:cli": 'bun build ./src/cli.ts --target=node --format=esm --outfile=dist/cli.js --banner "#!/usr/bin/env node" --external @opencode-ai/plugin --external @opencode-ai/sdk --external @opencode/schema --external effect --external zod --external @clack/prompts --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph',
|
|
5811
5825
|
"build:types": "tsc -p tsconfig.build.json",
|
|
5812
5826
|
clean: `node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"`,
|
|
5813
5827
|
test: "bun test",
|
|
5814
5828
|
"test:cov": "bun test --coverage",
|
|
5829
|
+
"verify:opencode-native": "bun scripts/verify-opencode-native.mjs",
|
|
5815
5830
|
"certify:shadow": "bun run scripts/certify-artifact.ts shadow",
|
|
5816
5831
|
"certify:release": "bun run scripts/certify-artifact.ts release",
|
|
5817
5832
|
"build:certificates": "bun run certify:shadow && bun run certify:release && bun run scripts/bundle-certificates.ts",
|
|
@@ -5833,10 +5848,12 @@ var package_default = {
|
|
|
5833
5848
|
"@langchain/langgraph": "1.4.12",
|
|
5834
5849
|
"@opencode-ai/plugin": "1.18.19",
|
|
5835
5850
|
"@opencode-ai/sdk": "1.18.19",
|
|
5851
|
+
"@opencode/schema": "2.0.3",
|
|
5836
5852
|
"@opentelemetry/api": "1.9.1",
|
|
5837
5853
|
"@opentelemetry/exporter-trace-otlp-http": "0.221.0",
|
|
5838
5854
|
"@opentelemetry/resources": "2.10.0",
|
|
5839
5855
|
"@opentelemetry/sdk-trace-base": "2.10.0",
|
|
5856
|
+
effect: "4.0.0-rc.112",
|
|
5840
5857
|
zod: "4.4.3"
|
|
5841
5858
|
},
|
|
5842
5859
|
devDependencies: {
|
|
@@ -6103,6 +6120,23 @@ var executionSetupMessages = {
|
|
|
6103
6120
|
`),
|
|
6104
6121
|
localOnlyTitle: "Local execution",
|
|
6105
6122
|
localRequiredError: "local execution requires an enabled local runtime",
|
|
6123
|
+
llamaSwap: {
|
|
6124
|
+
label: "llama-swap",
|
|
6125
|
+
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.",
|
|
6126
|
+
modelNotLoaded: (model) => `llama-swap model "${model}" is not positively verified as canonical and ready without an active routing profile. No substitute was selected.`,
|
|
6127
|
+
probeRequired: "llama-swap setup requires a fresh read-only residency probe before writing configuration. Manual model IDs cannot bypass this requirement.",
|
|
6128
|
+
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.",
|
|
6129
|
+
endpointMismatch: "llama-swap endpoint/provider mapping differs from the selected runtime. Reconcile the configured origin or /v1 URL and provider; no configuration was written.",
|
|
6130
|
+
modelIdentityUnknown: (model) => `llama-swap model "${model}" has no usable registered physical identity. Fix its model declaration; setup will not guess an alias.`,
|
|
6131
|
+
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.",
|
|
6132
|
+
declaredLimitsTitle: "Declared llama-swap limits",
|
|
6133
|
+
outputReduction: (model, previous, ceiling) => `${model}: reducing limit.output from ${previous} to ${ceiling} to respect the declared runtime ceiling. Smaller operator limits are preserved.`,
|
|
6134
|
+
outputReductionTitle: "Declared output ceiling",
|
|
6135
|
+
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.`
|
|
6136
|
+
},
|
|
6137
|
+
residencyV2Title: "Resident runtime transport compatibility",
|
|
6138
|
+
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.`,
|
|
6139
|
+
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.`,
|
|
6106
6140
|
lemonadeDetectedHint: (loaded, advertised) => `detected - ${loaded} confirmed loaded of ${advertised} advertised text model(s)`,
|
|
6107
6141
|
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.",
|
|
6108
6142
|
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.`,
|
|
@@ -6117,11 +6151,11 @@ var executionSetupMessages = {
|
|
|
6117
6151
|
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.",
|
|
6118
6152
|
lemonadeV2Title: "Lemonade transport compatibility",
|
|
6119
6153
|
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.`,
|
|
6120
|
-
lemonadeInventory: (instanceID, providerID, models) => {
|
|
6154
|
+
lemonadeInventory: (instanceID, providerID, models, runtime = "Lemonade") => {
|
|
6121
6155
|
const inventory = models.map((model) => `${model.modelID} (${model.loaded === true ? "loaded" : model.loaded === false ? "inactive" : "residency unknown"})`).join(", ");
|
|
6122
|
-
return
|
|
6156
|
+
return `${runtime} inventory ${instanceID} (provider ${providerID}): ${inventory || "no advertised text models"}. Only confirmed loaded text models count as ready.`;
|
|
6123
6157
|
},
|
|
6124
|
-
lemonadeAgentResidency: (agent, model, state) =>
|
|
6158
|
+
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.`,
|
|
6125
6159
|
mixedPrimaryQuestion: "Primary coordinator model",
|
|
6126
6160
|
mixedPrimaryLocal: (model) => `Use local thinking model ${model}`,
|
|
6127
6161
|
mixedPrimaryFrontier: "Choose a frontier model",
|
|
@@ -6225,6 +6259,8 @@ var executionPolicyMessages = {
|
|
|
6225
6259
|
primaryLocalMismatchRemedy: (sourceFile) => ` remedy: re-run \`openteam setup\` to regenerate ${sourceFile} and opencode.json from the same primary policy, then re-run \`openteam doctor\`.`
|
|
6226
6260
|
};
|
|
6227
6261
|
var doctorMessages = {
|
|
6262
|
+
defaultRuntimeSlots: (slots) => ` · ${slots} slot(s) (default)`,
|
|
6263
|
+
llamaSwapConcurrency: " llama-swap does not report per-model slots; openteam uses the configured runtime-wide concurrency limit. No per-model capacity was inferred.",
|
|
6228
6264
|
localModelLimits: {
|
|
6229
6265
|
section: " local model limits:",
|
|
6230
6266
|
healthy: " ✓ enabled local provider models declare valid output and context limits in opencode.json.",
|
|
@@ -6241,7 +6277,7 @@ var doctorMessages = {
|
|
|
6241
6277
|
invalidInput: "invalid limit.input (expected a positive integer token count)",
|
|
6242
6278
|
outputExceedsContext: "limit.output is greater than or equal to limit.context, so the configured context leaves no usable input window",
|
|
6243
6279
|
outputExceedsInput: "limit.input is less than or equal to the reserved output budget, so compaction would have no usable input threshold",
|
|
6244
|
-
outputRemedy: " remedy: re-run `openteam setup` to write openteam's 8 192
|
|
6280
|
+
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`.",
|
|
6245
6281
|
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`.",
|
|
6246
6282
|
detectedContextRemedy: " remedy: re-run `openteam setup` while the runtime is reachable to copy the detected usable context budget, then re-run `openteam doctor`.",
|
|
6247
6283
|
unknownContextRemedy: " remedy: re-run `openteam setup` to write the required context:0 sentinel; automatic compaction remains disabled until a positive context is configured.",
|
|
@@ -6333,7 +6369,7 @@ var NPM_LATEST_URL = `${NPM_ORIGIN}${NPM_PACKAGE_PATH}/latest`;
|
|
|
6333
6369
|
var FETCH_TIMEOUT_MS = 5000;
|
|
6334
6370
|
var MAX_RESPONSE_BYTES = 64 * 1024;
|
|
6335
6371
|
var MAX_FETCH_ATTEMPTS = 2;
|
|
6336
|
-
var
|
|
6372
|
+
var PLUGIN_KEYS = ["plugin", "plugins"];
|
|
6337
6373
|
var PRIVATE_DIRECTORY_MODE = 448;
|
|
6338
6374
|
var PRIVATE_FILE_MODE = 384;
|
|
6339
6375
|
var UPGRADE_BACKUP_GITIGNORE_CONTENT = `*
|
|
@@ -6387,6 +6423,8 @@ function extractSpecString(entry) {
|
|
|
6387
6423
|
if (isTuplePluginEntry(entry)) {
|
|
6388
6424
|
return entry[0];
|
|
6389
6425
|
}
|
|
6426
|
+
if (isRecord3(entry) && typeof entry.package === "string")
|
|
6427
|
+
return entry.package;
|
|
6390
6428
|
return;
|
|
6391
6429
|
}
|
|
6392
6430
|
function isLocalFileSpec(spec) {
|
|
@@ -6599,7 +6637,7 @@ function readJsoncValueEnd(src, start, limit) {
|
|
|
6599
6637
|
}
|
|
6600
6638
|
return i > start ? i : undefined;
|
|
6601
6639
|
}
|
|
6602
|
-
function findTopLevelPluginArray(raw) {
|
|
6640
|
+
function findTopLevelPluginArray(raw, pluginKey) {
|
|
6603
6641
|
let found;
|
|
6604
6642
|
let i = 0;
|
|
6605
6643
|
let depth = 0;
|
|
@@ -6646,7 +6684,7 @@ function findTopLevelPluginArray(raw) {
|
|
|
6646
6684
|
return;
|
|
6647
6685
|
}
|
|
6648
6686
|
i = token.end;
|
|
6649
|
-
if (key !==
|
|
6687
|
+
if (key !== pluginKey || depth !== 1) {
|
|
6650
6688
|
continue;
|
|
6651
6689
|
}
|
|
6652
6690
|
i = skipTrivia(raw, i);
|
|
@@ -6712,6 +6750,33 @@ function findEntrySpecStringSpan(raw, element, entry, expectedSpec) {
|
|
|
6712
6750
|
const span2 = { start: element.start, end: token2.end };
|
|
6713
6751
|
return readJsonStringValue(raw, span2) === expectedSpec ? span2 : undefined;
|
|
6714
6752
|
}
|
|
6753
|
+
if (isRecord3(entry) && typeof entry.package === "string") {
|
|
6754
|
+
let found;
|
|
6755
|
+
let i = skipTrivia(raw, element.start + 1, element.end);
|
|
6756
|
+
while (i < element.end && raw[i] !== "}") {
|
|
6757
|
+
const key = readStringToken(raw, i, element.end);
|
|
6758
|
+
if (key === undefined)
|
|
6759
|
+
return;
|
|
6760
|
+
const name = readJsonStringValue(raw, { start: i, end: key.end });
|
|
6761
|
+
i = skipTrivia(raw, key.end, element.end);
|
|
6762
|
+
if (raw[i] !== ":")
|
|
6763
|
+
return;
|
|
6764
|
+
i = skipTrivia(raw, i + 1, element.end);
|
|
6765
|
+
const end = readJsoncValueEnd(raw, i, element.end);
|
|
6766
|
+
if (end === undefined)
|
|
6767
|
+
return;
|
|
6768
|
+
if (name === "package") {
|
|
6769
|
+
const span2 = { start: i, end };
|
|
6770
|
+
found = readJsonStringValue(raw, span2) === expectedSpec ? span2 : undefined;
|
|
6771
|
+
}
|
|
6772
|
+
i = skipTrivia(raw, end, element.end);
|
|
6773
|
+
if (raw[i] === ",")
|
|
6774
|
+
i = skipTrivia(raw, i + 1, element.end);
|
|
6775
|
+
else if (raw[i] !== "}")
|
|
6776
|
+
return;
|
|
6777
|
+
}
|
|
6778
|
+
return found;
|
|
6779
|
+
}
|
|
6715
6780
|
if (!isTuplePluginEntry(entry)) {
|
|
6716
6781
|
return;
|
|
6717
6782
|
}
|
|
@@ -6739,20 +6804,23 @@ function validateEffectivePluginUpdate(path, nextContent, expectedManagedEntries
|
|
|
6739
6804
|
if (!parsed.ok) {
|
|
6740
6805
|
return parsed.error;
|
|
6741
6806
|
}
|
|
6742
|
-
const pluginValue = parsed.value[PLUGIN_KEY];
|
|
6743
|
-
if (!Array.isArray(pluginValue)) {
|
|
6744
|
-
return upgradeMessages.effectivePluginUpdateMismatch(path);
|
|
6745
|
-
}
|
|
6746
6807
|
let managedEntries = 0;
|
|
6747
6808
|
let updatedEntries = 0;
|
|
6748
|
-
for (const
|
|
6749
|
-
const
|
|
6750
|
-
if (
|
|
6809
|
+
for (const key of PLUGIN_KEYS) {
|
|
6810
|
+
const pluginValue = parsed.value[key];
|
|
6811
|
+
if (pluginValue === undefined)
|
|
6751
6812
|
continue;
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6813
|
+
if (!Array.isArray(pluginValue))
|
|
6814
|
+
return upgradeMessages.effectivePluginUpdateMismatch(path);
|
|
6815
|
+
for (const entry of pluginValue) {
|
|
6816
|
+
const spec = extractSpecString(entry);
|
|
6817
|
+
if (spec === undefined || !isOurSpec(spec)) {
|
|
6818
|
+
continue;
|
|
6819
|
+
}
|
|
6820
|
+
managedEntries += 1;
|
|
6821
|
+
if (spec === nextSpec) {
|
|
6822
|
+
updatedEntries += 1;
|
|
6823
|
+
}
|
|
6756
6824
|
}
|
|
6757
6825
|
}
|
|
6758
6826
|
return managedEntries === expectedManagedEntries && updatedEntries === expectedManagedEntries ? undefined : upgradeMessages.effectivePluginUpdateMismatch(path);
|
|
@@ -7013,70 +7081,52 @@ async function analyseConfigFiles(paths, port) {
|
|
|
7013
7081
|
parseError: parsed.error
|
|
7014
7082
|
};
|
|
7015
7083
|
}
|
|
7016
|
-
const
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
if (!Array.isArray(pluginValue)) {
|
|
7021
|
-
return {
|
|
7022
|
-
files,
|
|
7023
|
-
foundConfigFile,
|
|
7024
|
-
localOpenteamEntries,
|
|
7025
|
-
parseError: upgradeMessages.pluginFieldMustBeArray(path)
|
|
7026
|
-
};
|
|
7027
|
-
}
|
|
7028
|
-
const managedCandidates = [];
|
|
7029
|
-
for (let index = 0;index < pluginValue.length; index += 1) {
|
|
7030
|
-
const entry = pluginValue[index];
|
|
7031
|
-
const spec = extractSpecString(entry);
|
|
7032
|
-
if (spec === undefined) {
|
|
7084
|
+
const fileReplacements = [];
|
|
7085
|
+
for (const pluginKey of PLUGIN_KEYS) {
|
|
7086
|
+
const pluginValue = parsed.value[pluginKey];
|
|
7087
|
+
if (pluginValue === undefined) {
|
|
7033
7088
|
continue;
|
|
7034
7089
|
}
|
|
7035
|
-
if (
|
|
7036
|
-
|
|
7037
|
-
|
|
7090
|
+
if (!Array.isArray(pluginValue)) {
|
|
7091
|
+
return {
|
|
7092
|
+
files,
|
|
7093
|
+
foundConfigFile,
|
|
7094
|
+
localOpenteamEntries,
|
|
7095
|
+
parseError: upgradeMessages.pluginFieldMustBeArray(path)
|
|
7096
|
+
};
|
|
7097
|
+
}
|
|
7098
|
+
const managedCandidates = [];
|
|
7099
|
+
for (let index = 0;index < pluginValue.length; index += 1) {
|
|
7100
|
+
const entry = pluginValue[index];
|
|
7101
|
+
const spec = extractSpecString(entry);
|
|
7102
|
+
if (spec === undefined) {
|
|
7103
|
+
continue;
|
|
7104
|
+
}
|
|
7105
|
+
if (isLocalFileSpec(spec)) {
|
|
7106
|
+
const localKind = await port.classifyLocalPluginSpec({
|
|
7107
|
+
configPath: path,
|
|
7108
|
+
spec
|
|
7109
|
+
});
|
|
7110
|
+
if (localKind === "ours") {
|
|
7111
|
+
localOpenteamEntries.push({ path, spec });
|
|
7112
|
+
}
|
|
7113
|
+
continue;
|
|
7114
|
+
}
|
|
7115
|
+
if (!isOurSpec(spec)) {
|
|
7116
|
+
continue;
|
|
7117
|
+
}
|
|
7118
|
+
managedCandidates.push({
|
|
7119
|
+
currentVersion: currentPinnedVersion(spec),
|
|
7120
|
+
entry,
|
|
7121
|
+
index,
|
|
7038
7122
|
spec
|
|
7039
7123
|
});
|
|
7040
|
-
if (localKind === "ours") {
|
|
7041
|
-
localOpenteamEntries.push({ path, spec });
|
|
7042
|
-
}
|
|
7043
|
-
continue;
|
|
7044
7124
|
}
|
|
7045
|
-
if (
|
|
7125
|
+
if (managedCandidates.length === 0) {
|
|
7046
7126
|
continue;
|
|
7047
7127
|
}
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
entry,
|
|
7051
|
-
index,
|
|
7052
|
-
spec
|
|
7053
|
-
});
|
|
7054
|
-
}
|
|
7055
|
-
if (managedCandidates.length === 0) {
|
|
7056
|
-
continue;
|
|
7057
|
-
}
|
|
7058
|
-
const pluginArray = findTopLevelPluginArray(content);
|
|
7059
|
-
if (pluginArray === undefined) {
|
|
7060
|
-
return {
|
|
7061
|
-
files,
|
|
7062
|
-
foundConfigFile,
|
|
7063
|
-
localOpenteamEntries,
|
|
7064
|
-
parseError: upgradeMessages.pluginArrayEditUnavailable(path)
|
|
7065
|
-
};
|
|
7066
|
-
}
|
|
7067
|
-
const elementSpans = scanTopLevelArrayElements(content, pluginArray.start, pluginArray.end);
|
|
7068
|
-
if (elementSpans === undefined || elementSpans.length !== pluginValue.length) {
|
|
7069
|
-
return {
|
|
7070
|
-
files,
|
|
7071
|
-
foundConfigFile,
|
|
7072
|
-
localOpenteamEntries,
|
|
7073
|
-
parseError: upgradeMessages.pluginArrayEditUnavailable(path)
|
|
7074
|
-
};
|
|
7075
|
-
}
|
|
7076
|
-
const replacements = [];
|
|
7077
|
-
for (const candidate of managedCandidates) {
|
|
7078
|
-
const elementSpan = elementSpans[candidate.index];
|
|
7079
|
-
if (elementSpan === undefined) {
|
|
7128
|
+
const pluginArray = findTopLevelPluginArray(content, pluginKey);
|
|
7129
|
+
if (pluginArray === undefined) {
|
|
7080
7130
|
return {
|
|
7081
7131
|
files,
|
|
7082
7132
|
foundConfigFile,
|
|
@@ -7084,8 +7134,8 @@ async function analyseConfigFiles(paths, port) {
|
|
|
7084
7134
|
parseError: upgradeMessages.pluginArrayEditUnavailable(path)
|
|
7085
7135
|
};
|
|
7086
7136
|
}
|
|
7087
|
-
const
|
|
7088
|
-
if (
|
|
7137
|
+
const elementSpans = scanTopLevelArrayElements(content, pluginArray.start, pluginArray.end);
|
|
7138
|
+
if (elementSpans === undefined || elementSpans.length !== pluginValue.length) {
|
|
7089
7139
|
return {
|
|
7090
7140
|
files,
|
|
7091
7141
|
foundConfigFile,
|
|
@@ -7093,16 +7143,40 @@ async function analyseConfigFiles(paths, port) {
|
|
|
7093
7143
|
parseError: upgradeMessages.pluginArrayEditUnavailable(path)
|
|
7094
7144
|
};
|
|
7095
7145
|
}
|
|
7096
|
-
replacements
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7146
|
+
const replacements = [];
|
|
7147
|
+
for (const candidate of managedCandidates) {
|
|
7148
|
+
const elementSpan = elementSpans[candidate.index];
|
|
7149
|
+
if (elementSpan === undefined) {
|
|
7150
|
+
return {
|
|
7151
|
+
files,
|
|
7152
|
+
foundConfigFile,
|
|
7153
|
+
localOpenteamEntries,
|
|
7154
|
+
parseError: upgradeMessages.pluginArrayEditUnavailable(path)
|
|
7155
|
+
};
|
|
7156
|
+
}
|
|
7157
|
+
const specSpan = findEntrySpecStringSpan(content, elementSpan, candidate.entry, candidate.spec);
|
|
7158
|
+
if (specSpan === undefined) {
|
|
7159
|
+
return {
|
|
7160
|
+
files,
|
|
7161
|
+
foundConfigFile,
|
|
7162
|
+
localOpenteamEntries,
|
|
7163
|
+
parseError: upgradeMessages.pluginArrayEditUnavailable(path)
|
|
7164
|
+
};
|
|
7165
|
+
}
|
|
7166
|
+
replacements.push({
|
|
7167
|
+
...specSpan,
|
|
7168
|
+
currentVersion: candidate.currentVersion
|
|
7169
|
+
});
|
|
7170
|
+
}
|
|
7171
|
+
fileReplacements.push(...replacements);
|
|
7100
7172
|
}
|
|
7173
|
+
if (fileReplacements.length === 0)
|
|
7174
|
+
continue;
|
|
7101
7175
|
files.push({
|
|
7102
|
-
currentVersions:
|
|
7176
|
+
currentVersions: fileReplacements.map((replacement) => replacement.currentVersion),
|
|
7103
7177
|
path,
|
|
7104
7178
|
originalContent: content,
|
|
7105
|
-
replacements
|
|
7179
|
+
replacements: fileReplacements
|
|
7106
7180
|
});
|
|
7107
7181
|
}
|
|
7108
7182
|
return {
|
|
@@ -7830,15 +7904,14 @@ function mergeOpencodeConfigs(base, override) {
|
|
|
7830
7904
|
return result;
|
|
7831
7905
|
}
|
|
7832
7906
|
|
|
7833
|
-
// src/graph/certificate.ts
|
|
7834
|
-
import { z as z17 } from "zod";
|
|
7835
|
-
|
|
7836
7907
|
// src/contract/opencode.ts
|
|
7837
7908
|
var SUPPORTED_OPENCODE_VERSIONS = [
|
|
7838
7909
|
"1.17.13",
|
|
7839
7910
|
"1.18.18",
|
|
7840
7911
|
"1.18.19"
|
|
7841
7912
|
];
|
|
7913
|
+
var NATIVE_OPENCODE_VERSION = "2.0.5";
|
|
7914
|
+
var NATIVE_CERTIFICATE_DIRECTORY = `opencode-${NATIVE_OPENCODE_VERSION}`;
|
|
7842
7915
|
var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)/;
|
|
7843
7916
|
function parseSemver(version) {
|
|
7844
7917
|
const normalized = version.trim().replace(/^[=^~v]+/, "");
|
|
@@ -7864,12 +7937,15 @@ function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
|
|
|
7864
7937
|
} catch {
|
|
7865
7938
|
return false;
|
|
7866
7939
|
}
|
|
7867
|
-
if (certified.major !== live.major || certified.minor !== live.minor) {
|
|
7940
|
+
if (certified.major !== 1 || certified.major !== live.major || certified.minor !== live.minor) {
|
|
7868
7941
|
return false;
|
|
7869
7942
|
}
|
|
7870
7943
|
return live.patch >= certified.patch;
|
|
7871
7944
|
}
|
|
7872
7945
|
|
|
7946
|
+
// src/graph/certificate.ts
|
|
7947
|
+
import { z as z17 } from "zod";
|
|
7948
|
+
|
|
7873
7949
|
// src/graph/soakLedger.ts
|
|
7874
7950
|
import { z as z16 } from "zod";
|
|
7875
7951
|
var Sha256Schema2 = z16.string().regex(/^[0-9a-f]{64}$/);
|
|
@@ -9953,14 +10029,12 @@ function ensureFoundryV1BaseURL(endpoint) {
|
|
|
9953
10029
|
}
|
|
9954
10030
|
}
|
|
9955
10031
|
|
|
9956
|
-
// src/local/lemonadeResidency.ts
|
|
9957
|
-
import { z as z19 } from "zod";
|
|
9958
|
-
|
|
9959
10032
|
// src/messages/modelResidency.ts
|
|
9960
10033
|
var reasons = {
|
|
9961
10034
|
unknown: "fresh residency metadata is unavailable, malformed, or ambiguous",
|
|
9962
10035
|
inactive: "the requested model is not verified as resident and serving",
|
|
9963
10036
|
identity: "the requested model identity can be rewritten or is ambiguous",
|
|
10037
|
+
"authentication-unsupported": "authentication is required but unsupported by this integration",
|
|
9964
10038
|
destination: "the request destination does not match its configured runtime",
|
|
9965
10039
|
transport: "this provider execution path cannot enforce the residency guard",
|
|
9966
10040
|
request: "this is not a supported, explicit model inference request",
|
|
@@ -9969,22 +10043,328 @@ var reasons = {
|
|
|
9969
10043
|
configuration: "the residency guard configuration is invalid"
|
|
9970
10044
|
};
|
|
9971
10045
|
var modelResidencyMessages = {
|
|
9972
|
-
blocked: (reason) => `[openteam]
|
|
9973
|
-
providerDisabled: (providerID) => `[openteam]
|
|
9974
|
-
extractionRuntimeUnavailable: "[openteam] Memory extraction requires one enabled local runtime matching the configured extraction provider."
|
|
10046
|
+
blocked: (reason, runtimeLabel = "Lemonade", detail) => `[openteam] ${runtimeLabel} model request blocked: ${reasons[reason]}${detail === undefined ? "" : ` (${detail})`}.`,
|
|
10047
|
+
providerDisabled: (providerID, runtimeLabel = "Lemonade") => `[openteam] ${runtimeLabel} provider "${providerID}" is disabled because its configured transport or runtime mapping cannot enforce loaded-only requests.`,
|
|
10048
|
+
extractionRuntimeUnavailable: "[openteam] Memory extraction requires one enabled local runtime matching the configured extraction provider.",
|
|
10049
|
+
extractionRuntimeUnavailableFor: (runtimeLabel) => `[openteam] Memory extraction requires one enabled ${runtimeLabel} local runtime matching the configured extraction provider.`
|
|
10050
|
+
};
|
|
10051
|
+
var llamaSwapResidencyMessages = {
|
|
10052
|
+
catalogModelDetail: (id, detail) => `catalog model "${id}" ${detail}`,
|
|
10053
|
+
invalidCatalogField: (field) => `declares an invalid ${field}`,
|
|
10054
|
+
catalogEntryMustExposeStringId: "catalog entries must expose a string id",
|
|
10055
|
+
catalogModelIdsMustBeNonEmptyTrimmedStrings: "catalog model ids must be non-empty, trimmed strings",
|
|
10056
|
+
expectedDataArray: (path2) => `GET ${path2} must return an object with a data array`,
|
|
10057
|
+
duplicateCanonicalModelId: (modelID, path2) => `duplicate canonical model id "${modelID}" in ${path2}`,
|
|
10058
|
+
httpFailure: (path2, status) => `GET ${path2} failed with HTTP ${status}`,
|
|
10059
|
+
unverifiableResponse: (path2) => `GET ${path2} failed before a verifiable response`,
|
|
10060
|
+
expectedProfilesArray: (path2) => `GET ${path2} must return an object with a profiles array`,
|
|
10061
|
+
activeProfileRewrite: (profile) => `active profile "${profile}" can rewrite canonical model routing`,
|
|
10062
|
+
invalidActiveProfileValue: (path2) => `GET ${path2} reported an invalid active profile value`,
|
|
10063
|
+
expectedRunningArray: (path2) => `GET ${path2} must return an object with a running array`,
|
|
10064
|
+
malformedRunningEvidence: (path2) => `GET ${path2} returned malformed model/state evidence`,
|
|
10065
|
+
duplicateRunningEvidence: (path2, modelID) => `GET ${path2} returned duplicate evidence for "${modelID}"`
|
|
9975
10066
|
};
|
|
9976
10067
|
|
|
9977
|
-
// src/local/
|
|
10068
|
+
// src/local/residency.ts
|
|
9978
10069
|
class ModelResidencyError extends Error {
|
|
9979
10070
|
code;
|
|
9980
10071
|
statusCode = 400;
|
|
9981
10072
|
isRetryable = false;
|
|
9982
|
-
constructor(code) {
|
|
9983
|
-
super(modelResidencyMessages.blocked(code));
|
|
10073
|
+
constructor(code, runtimeLabel = "Lemonade", detail) {
|
|
10074
|
+
super(modelResidencyMessages.blocked(code, runtimeLabel, detail));
|
|
9984
10075
|
this.code = code;
|
|
9985
10076
|
this.name = "ModelResidencyError";
|
|
9986
10077
|
}
|
|
9987
10078
|
}
|
|
10079
|
+
async function withResidencyDeadline(operation, options) {
|
|
10080
|
+
const timeoutMs = options.timeoutMs ?? 5000;
|
|
10081
|
+
const runtimeLabel = options.runtimeLabel;
|
|
10082
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
10083
|
+
throw new ModelResidencyError("configuration", runtimeLabel);
|
|
10084
|
+
}
|
|
10085
|
+
const controller = new AbortController;
|
|
10086
|
+
const cancel = () => controller.abort(new ModelResidencyError("cancelled", runtimeLabel));
|
|
10087
|
+
if (options.signal?.aborted)
|
|
10088
|
+
throw new ModelResidencyError("cancelled", runtimeLabel);
|
|
10089
|
+
options.signal?.addEventListener("abort", cancel, { once: true });
|
|
10090
|
+
const schedule = options.scheduleTimeout ?? ((callback, ms) => {
|
|
10091
|
+
const timer = setTimeout(callback, ms);
|
|
10092
|
+
return () => clearTimeout(timer);
|
|
10093
|
+
});
|
|
10094
|
+
let onAbort = () => {};
|
|
10095
|
+
const aborted = new Promise((_, reject) => {
|
|
10096
|
+
onAbort = () => reject(controller.signal.reason);
|
|
10097
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
10098
|
+
});
|
|
10099
|
+
const clearTimer = schedule(() => controller.abort(new ModelResidencyError("timeout", runtimeLabel)), timeoutMs);
|
|
10100
|
+
try {
|
|
10101
|
+
controller.signal.throwIfAborted();
|
|
10102
|
+
return await Promise.race([operation(controller.signal), aborted]);
|
|
10103
|
+
} finally {
|
|
10104
|
+
clearTimer();
|
|
10105
|
+
options.signal?.removeEventListener("abort", cancel);
|
|
10106
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
10107
|
+
controller.abort();
|
|
10108
|
+
}
|
|
10109
|
+
}
|
|
10110
|
+
|
|
10111
|
+
// src/local/llamaSwapResidency.ts
|
|
10112
|
+
function isObject2(value) {
|
|
10113
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10114
|
+
}
|
|
10115
|
+
function readPositiveSafeInteger(value) {
|
|
10116
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
|
|
10117
|
+
}
|
|
10118
|
+
function readSupportsTools2(model) {
|
|
10119
|
+
const direct = readBoolean2(model, [
|
|
10120
|
+
"tool_call",
|
|
10121
|
+
"tool_calls",
|
|
10122
|
+
"toolCalling",
|
|
10123
|
+
"supportsToolCalling",
|
|
10124
|
+
"supports_tools",
|
|
10125
|
+
"supportsTools"
|
|
10126
|
+
]);
|
|
10127
|
+
if (direct !== undefined) {
|
|
10128
|
+
return direct;
|
|
10129
|
+
}
|
|
10130
|
+
const capabilities = readCapabilityBoolean2(model.capabilities);
|
|
10131
|
+
if (capabilities !== undefined) {
|
|
10132
|
+
return capabilities;
|
|
10133
|
+
}
|
|
10134
|
+
const features = readCapabilityBoolean2(model.features);
|
|
10135
|
+
if (features !== undefined) {
|
|
10136
|
+
return features;
|
|
10137
|
+
}
|
|
10138
|
+
const labels = readCapabilityBoolean2(model.labels);
|
|
10139
|
+
if (labels !== undefined) {
|
|
10140
|
+
return labels;
|
|
10141
|
+
}
|
|
10142
|
+
return "unknown";
|
|
10143
|
+
}
|
|
10144
|
+
function readCapabilityBoolean2(value) {
|
|
10145
|
+
if (Array.isArray(value)) {
|
|
10146
|
+
if (value.some((entry) => typeof entry === "string" && ["tools", "tool_call", "tool-calling", "function_calling"].includes(entry))) {
|
|
10147
|
+
return true;
|
|
10148
|
+
}
|
|
10149
|
+
return;
|
|
10150
|
+
}
|
|
10151
|
+
if (!isObject2(value)) {
|
|
10152
|
+
return;
|
|
10153
|
+
}
|
|
10154
|
+
return readBoolean2(value, [
|
|
10155
|
+
"tools",
|
|
10156
|
+
"tool_call",
|
|
10157
|
+
"tool_calls",
|
|
10158
|
+
"toolCalling",
|
|
10159
|
+
"function_calling",
|
|
10160
|
+
"supportsToolCalling"
|
|
10161
|
+
]);
|
|
10162
|
+
}
|
|
10163
|
+
function readBoolean2(value, keys) {
|
|
10164
|
+
for (const key of keys) {
|
|
10165
|
+
if (typeof value[key] === "boolean") {
|
|
10166
|
+
return value[key];
|
|
10167
|
+
}
|
|
10168
|
+
}
|
|
10169
|
+
return;
|
|
10170
|
+
}
|
|
10171
|
+
function modelDetail(id, detail) {
|
|
10172
|
+
return llamaSwapResidencyMessages.catalogModelDetail(id, detail);
|
|
10173
|
+
}
|
|
10174
|
+
function llamaSwapBaseURL(value) {
|
|
10175
|
+
let url;
|
|
10176
|
+
try {
|
|
10177
|
+
url = new URL(value);
|
|
10178
|
+
} catch {
|
|
10179
|
+
throw new ModelResidencyError("destination", "llama-swap");
|
|
10180
|
+
}
|
|
10181
|
+
if (url.protocol !== "http:" && url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") {
|
|
10182
|
+
throw new ModelResidencyError("destination", "llama-swap");
|
|
10183
|
+
}
|
|
10184
|
+
if (url.pathname === "/" || url.pathname === "") {
|
|
10185
|
+
return `${url.origin}/v1`;
|
|
10186
|
+
}
|
|
10187
|
+
if (url.pathname === "/v1" || url.pathname === "/v1/") {
|
|
10188
|
+
return `${url.origin}/v1`;
|
|
10189
|
+
}
|
|
10190
|
+
throw new ModelResidencyError("destination", "llama-swap");
|
|
10191
|
+
}
|
|
10192
|
+
function parseCanonicalType(entry, id) {
|
|
10193
|
+
if (!isObject2(entry.meta) || !isObject2(entry.meta.llamaswap)) {
|
|
10194
|
+
return;
|
|
10195
|
+
}
|
|
10196
|
+
const type = entry.meta.llamaswap.type;
|
|
10197
|
+
if (type === undefined) {
|
|
10198
|
+
return;
|
|
10199
|
+
}
|
|
10200
|
+
if (typeof type !== "string" || type.length === 0 || type !== type.trim()) {
|
|
10201
|
+
throw new ModelResidencyError("unknown", "llama-swap", modelDetail(id, llamaSwapResidencyMessages.invalidCatalogField("meta.llamaswap.type")));
|
|
10202
|
+
}
|
|
10203
|
+
return type === "model" ? "model" : undefined;
|
|
10204
|
+
}
|
|
10205
|
+
function readContextCandidates(entry) {
|
|
10206
|
+
const values = [
|
|
10207
|
+
readPositiveSafeInteger(entry.context_length),
|
|
10208
|
+
readPositiveSafeInteger(entry.context_window),
|
|
10209
|
+
isObject2(entry.meta) ? readPositiveSafeInteger(entry.meta.n_ctx) : undefined
|
|
10210
|
+
];
|
|
10211
|
+
return values.filter((value) => value !== undefined);
|
|
10212
|
+
}
|
|
10213
|
+
function readDeclaredMaxOutput(entry, id) {
|
|
10214
|
+
if (!isObject2(entry.meta) || !isObject2(entry.meta.llamaswap)) {
|
|
10215
|
+
return;
|
|
10216
|
+
}
|
|
10217
|
+
if (!Object.hasOwn(entry.meta.llamaswap, "max_output_tokens")) {
|
|
10218
|
+
return;
|
|
10219
|
+
}
|
|
10220
|
+
const maxOutputTokens = readPositiveSafeInteger(entry.meta.llamaswap.max_output_tokens);
|
|
10221
|
+
if (maxOutputTokens === undefined) {
|
|
10222
|
+
throw new ModelResidencyError("unknown", "llama-swap", modelDetail(id, llamaSwapResidencyMessages.invalidCatalogField("meta.llamaswap.max_output_tokens")));
|
|
10223
|
+
}
|
|
10224
|
+
return maxOutputTokens;
|
|
10225
|
+
}
|
|
10226
|
+
function parseCatalogModel(value) {
|
|
10227
|
+
if (!isObject2(value) || typeof value.id !== "string") {
|
|
10228
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.catalogEntryMustExposeStringId);
|
|
10229
|
+
}
|
|
10230
|
+
const id = value.id;
|
|
10231
|
+
if (id.length === 0 || id !== id.trim()) {
|
|
10232
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.catalogModelIdsMustBeNonEmptyTrimmedStrings);
|
|
10233
|
+
}
|
|
10234
|
+
if (parseCanonicalType(value, id) !== "model") {
|
|
10235
|
+
return;
|
|
10236
|
+
}
|
|
10237
|
+
const model = {
|
|
10238
|
+
modelID: id,
|
|
10239
|
+
supportsTools: readSupportsTools2(value)
|
|
10240
|
+
};
|
|
10241
|
+
const contexts = readContextCandidates(value);
|
|
10242
|
+
if (contexts.length > 0) {
|
|
10243
|
+
model.contextWindow = Math.min(...contexts);
|
|
10244
|
+
model.contextWindowProvenance = "declared";
|
|
10245
|
+
}
|
|
10246
|
+
const maxOutputTokens = readDeclaredMaxOutput(value, id);
|
|
10247
|
+
if (maxOutputTokens !== undefined) {
|
|
10248
|
+
model.maxOutputTokens = maxOutputTokens;
|
|
10249
|
+
model.maxOutputTokensProvenance = "declared";
|
|
10250
|
+
}
|
|
10251
|
+
return model;
|
|
10252
|
+
}
|
|
10253
|
+
function parseCatalog(payload) {
|
|
10254
|
+
if (!isObject2(payload) || !Array.isArray(payload.data)) {
|
|
10255
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.expectedDataArray("/v1/models"));
|
|
10256
|
+
}
|
|
10257
|
+
const seen = new Set;
|
|
10258
|
+
const models = [];
|
|
10259
|
+
for (const entry of payload.data) {
|
|
10260
|
+
const model = parseCatalogModel(entry);
|
|
10261
|
+
if (model === undefined) {
|
|
10262
|
+
continue;
|
|
10263
|
+
}
|
|
10264
|
+
if (seen.has(model.modelID)) {
|
|
10265
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.duplicateCanonicalModelId(model.modelID, "/v1/models"));
|
|
10266
|
+
}
|
|
10267
|
+
seen.add(model.modelID);
|
|
10268
|
+
models.push(model);
|
|
10269
|
+
}
|
|
10270
|
+
return models;
|
|
10271
|
+
}
|
|
10272
|
+
async function metadata(url, fetch, signal, headers) {
|
|
10273
|
+
signal.throwIfAborted();
|
|
10274
|
+
try {
|
|
10275
|
+
const response = await fetch(url, {
|
|
10276
|
+
method: "GET",
|
|
10277
|
+
headers,
|
|
10278
|
+
signal,
|
|
10279
|
+
redirect: "error",
|
|
10280
|
+
cache: "no-store"
|
|
10281
|
+
});
|
|
10282
|
+
signal.throwIfAborted();
|
|
10283
|
+
if (response.status === 401 || response.status === 403) {
|
|
10284
|
+
throw new ModelResidencyError("authentication-unsupported", "llama-swap");
|
|
10285
|
+
}
|
|
10286
|
+
if (!response.ok || response.redirected || response.url !== "" && response.url !== url) {
|
|
10287
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.httpFailure(new URL(url).pathname, response.status));
|
|
10288
|
+
}
|
|
10289
|
+
return await response.json();
|
|
10290
|
+
} catch (error) {
|
|
10291
|
+
signal.throwIfAborted();
|
|
10292
|
+
if (error instanceof ModelResidencyError) {
|
|
10293
|
+
throw error;
|
|
10294
|
+
}
|
|
10295
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.unverifiableResponse(new URL(url).pathname));
|
|
10296
|
+
}
|
|
10297
|
+
}
|
|
10298
|
+
function readProfiles(payload) {
|
|
10299
|
+
if (!isObject2(payload) || !Array.isArray(payload.profiles)) {
|
|
10300
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.expectedProfilesArray("/api/profiles"));
|
|
10301
|
+
}
|
|
10302
|
+
if (payload.active === null) {
|
|
10303
|
+
return;
|
|
10304
|
+
}
|
|
10305
|
+
if (typeof payload.active === "string" && payload.active.length > 0 && payload.active === payload.active.trim()) {
|
|
10306
|
+
throw new ModelResidencyError("identity", "llama-swap", llamaSwapResidencyMessages.activeProfileRewrite(payload.active));
|
|
10307
|
+
}
|
|
10308
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.invalidActiveProfileValue("/api/profiles"));
|
|
10309
|
+
}
|
|
10310
|
+
function readRunning(payload, modelIDs) {
|
|
10311
|
+
if (!isObject2(payload) || !Array.isArray(payload.running)) {
|
|
10312
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.expectedRunningArray("/running"));
|
|
10313
|
+
}
|
|
10314
|
+
const result = new Map;
|
|
10315
|
+
for (const modelID of modelIDs) {
|
|
10316
|
+
result.set(modelID, { loaded: false });
|
|
10317
|
+
}
|
|
10318
|
+
const seen = new Set;
|
|
10319
|
+
for (const entry of payload.running) {
|
|
10320
|
+
if (!isObject2(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()) {
|
|
10321
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.malformedRunningEvidence("/running"));
|
|
10322
|
+
}
|
|
10323
|
+
if (seen.has(entry.model)) {
|
|
10324
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.duplicateRunningEvidence("/running", entry.model));
|
|
10325
|
+
}
|
|
10326
|
+
seen.add(entry.model);
|
|
10327
|
+
if (!modelIDs.has(entry.model)) {
|
|
10328
|
+
continue;
|
|
10329
|
+
}
|
|
10330
|
+
result.set(entry.model, { loaded: entry.state === "ready" });
|
|
10331
|
+
}
|
|
10332
|
+
return result;
|
|
10333
|
+
}
|
|
10334
|
+
function requestHeaders(headers) {
|
|
10335
|
+
const result = new Headers(headers);
|
|
10336
|
+
result.delete("content-type");
|
|
10337
|
+
result.delete("content-length");
|
|
10338
|
+
result.set("accept", "application/json");
|
|
10339
|
+
result.set("cache-control", "no-cache, no-store");
|
|
10340
|
+
result.delete("if-none-match");
|
|
10341
|
+
result.delete("if-modified-since");
|
|
10342
|
+
return result;
|
|
10343
|
+
}
|
|
10344
|
+
function llamaSwapResidencyOptions(options) {
|
|
10345
|
+
return { ...options, runtimeLabel: "llama-swap" };
|
|
10346
|
+
}
|
|
10347
|
+
async function readLlamaSwapCatalog(baseURL, fetch, options = {}) {
|
|
10348
|
+
const base = llamaSwapBaseURL(baseURL);
|
|
10349
|
+
const headers = requestHeaders(options.headers);
|
|
10350
|
+
return withResidencyDeadline(async (signal) => parseCatalog(await metadata(`${base}/models`, fetch, signal, headers)), llamaSwapResidencyOptions(options));
|
|
10351
|
+
}
|
|
10352
|
+
async function readLlamaSwapResidency(baseURL, fetch, options = {}) {
|
|
10353
|
+
const base = llamaSwapBaseURL(baseURL);
|
|
10354
|
+
const origin = new URL(base).origin;
|
|
10355
|
+
const headers = requestHeaders(options.headers);
|
|
10356
|
+
return withResidencyDeadline(async (signal) => {
|
|
10357
|
+
const models = await readLlamaSwapCatalog(base, fetch, {
|
|
10358
|
+
...llamaSwapResidencyOptions(options),
|
|
10359
|
+
signal
|
|
10360
|
+
});
|
|
10361
|
+
readProfiles(await metadata(`${origin}/api/profiles`, fetch, signal, headers));
|
|
10362
|
+
return readRunning(await metadata(`${origin}/running`, fetch, signal, headers), new Set(models.map((model) => model.modelID)));
|
|
10363
|
+
}, llamaSwapResidencyOptions(options));
|
|
10364
|
+
}
|
|
10365
|
+
|
|
10366
|
+
// src/local/lemonadeResidency.ts
|
|
10367
|
+
import { z as z19 } from "zod";
|
|
9988
10368
|
var modelName = z19.string().min(1).refine((name) => name === name.trim());
|
|
9989
10369
|
var LoadedModelSchema = z19.object({
|
|
9990
10370
|
model_name: modelName,
|
|
@@ -10121,37 +10501,7 @@ function lemonadeBaseURL(value) {
|
|
|
10121
10501
|
}
|
|
10122
10502
|
return `${url.origin}${url.pathname.replace(/\/$/, "")}`;
|
|
10123
10503
|
}
|
|
10124
|
-
async function
|
|
10125
|
-
const timeoutMs = options.timeoutMs ?? 5000;
|
|
10126
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
10127
|
-
throw new ModelResidencyError("configuration");
|
|
10128
|
-
}
|
|
10129
|
-
const controller = new AbortController;
|
|
10130
|
-
const cancel = () => controller.abort(new ModelResidencyError("cancelled"));
|
|
10131
|
-
if (options.signal?.aborted)
|
|
10132
|
-
throw new ModelResidencyError("cancelled");
|
|
10133
|
-
options.signal?.addEventListener("abort", cancel, { once: true });
|
|
10134
|
-
const schedule = options.scheduleTimeout ?? ((callback, ms) => {
|
|
10135
|
-
const timer = setTimeout(callback, ms);
|
|
10136
|
-
return () => clearTimeout(timer);
|
|
10137
|
-
});
|
|
10138
|
-
let onAbort = () => {};
|
|
10139
|
-
const aborted = new Promise((_, reject) => {
|
|
10140
|
-
onAbort = () => reject(controller.signal.reason);
|
|
10141
|
-
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
10142
|
-
});
|
|
10143
|
-
const clearTimer = schedule(() => controller.abort(new ModelResidencyError("timeout")), timeoutMs);
|
|
10144
|
-
try {
|
|
10145
|
-
controller.signal.throwIfAborted();
|
|
10146
|
-
return await Promise.race([operation(controller.signal), aborted]);
|
|
10147
|
-
} finally {
|
|
10148
|
-
clearTimer();
|
|
10149
|
-
options.signal?.removeEventListener("abort", cancel);
|
|
10150
|
-
controller.signal.removeEventListener("abort", onAbort);
|
|
10151
|
-
controller.abort();
|
|
10152
|
-
}
|
|
10153
|
-
}
|
|
10154
|
-
async function metadata(url, fetch, signal, headers) {
|
|
10504
|
+
async function metadata2(url, fetch, signal, headers) {
|
|
10155
10505
|
signal.throwIfAborted();
|
|
10156
10506
|
let payload;
|
|
10157
10507
|
try {
|
|
@@ -10184,8 +10534,8 @@ async function readLemonadeResidency(baseURL, fetch, options = {}) {
|
|
|
10184
10534
|
headers.delete("if-none-match");
|
|
10185
10535
|
headers.delete("if-modified-since");
|
|
10186
10536
|
return withResidencyDeadline(async (signal) => {
|
|
10187
|
-
const aliases = await
|
|
10188
|
-
const health = await
|
|
10537
|
+
const aliases = await metadata2(`${new URL(base).origin}/internal/aliases`, fetch, signal, headers);
|
|
10538
|
+
const health = await metadata2(`${base}/health`, fetch, signal, headers);
|
|
10189
10539
|
return parseResidency(health, aliases);
|
|
10190
10540
|
}, options);
|
|
10191
10541
|
}
|
|
@@ -10256,11 +10606,11 @@ async function listLemonadeModels(baseURL, fetch) {
|
|
|
10256
10606
|
}
|
|
10257
10607
|
function readNonTextModelIDs(payload) {
|
|
10258
10608
|
const ids = new Set;
|
|
10259
|
-
if (!
|
|
10609
|
+
if (!isObject3(payload) || !Array.isArray(payload.data)) {
|
|
10260
10610
|
return ids;
|
|
10261
10611
|
}
|
|
10262
10612
|
for (const entry of payload.data) {
|
|
10263
|
-
if (!
|
|
10613
|
+
if (!isObject3(entry) || typeof entry.id !== "string") {
|
|
10264
10614
|
continue;
|
|
10265
10615
|
}
|
|
10266
10616
|
if (isNonTextModel(entry)) {
|
|
@@ -10298,7 +10648,7 @@ function readLoadedContextWindow(entry) {
|
|
|
10298
10648
|
}
|
|
10299
10649
|
function slotsFromLlamacppArgs(entry) {
|
|
10300
10650
|
const options = entry.recipe_options;
|
|
10301
|
-
if (!
|
|
10651
|
+
if (!isObject3(options) || typeof options.llamacpp_args !== "string") {
|
|
10302
10652
|
return;
|
|
10303
10653
|
}
|
|
10304
10654
|
const match = PARALLEL_ARG.exec(options.llamacpp_args);
|
|
@@ -10334,7 +10684,7 @@ function slotsFromLaunchCommand(entry) {
|
|
|
10334
10684
|
}
|
|
10335
10685
|
function contextWindowFromRecipeOptions(entry) {
|
|
10336
10686
|
const options = entry.recipe_options;
|
|
10337
|
-
if (!
|
|
10687
|
+
if (!isObject3(options) || !hasLlamacppEvidence(entry)) {
|
|
10338
10688
|
return;
|
|
10339
10689
|
}
|
|
10340
10690
|
const direct = positiveIntegerValue(options.ctx_size);
|
|
@@ -10394,7 +10744,7 @@ function hasLlamacppEvidence(entry) {
|
|
|
10394
10744
|
return true;
|
|
10395
10745
|
}
|
|
10396
10746
|
const options = entry.recipe_options;
|
|
10397
|
-
if (
|
|
10747
|
+
if (isObject3(options) && typeof options.llamacpp_args === "string") {
|
|
10398
10748
|
return true;
|
|
10399
10749
|
}
|
|
10400
10750
|
return Array.isArray(entry.launch_command) && launchCommandLooksLikeLlamacpp(entry.launch_command);
|
|
@@ -10450,10 +10800,57 @@ function enrichWithLoadState(models, loaded) {
|
|
|
10450
10800
|
};
|
|
10451
10801
|
});
|
|
10452
10802
|
}
|
|
10453
|
-
function
|
|
10803
|
+
function isObject3(value) {
|
|
10454
10804
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10455
10805
|
}
|
|
10456
10806
|
|
|
10807
|
+
// src/local/llamaSwap.ts
|
|
10808
|
+
var LLAMA_SWAP_DEFAULT_BASE_URL = "http://localhost:8080/v1";
|
|
10809
|
+
function withLoadedState(models, residency) {
|
|
10810
|
+
return models.map((model) => {
|
|
10811
|
+
const loaded = residency.get(model.modelID)?.loaded;
|
|
10812
|
+
return loaded === undefined ? model : { ...model, loaded };
|
|
10813
|
+
});
|
|
10814
|
+
}
|
|
10815
|
+
function createLlamaSwapAdapter() {
|
|
10816
|
+
const listModels = async (options) => {
|
|
10817
|
+
const baseURL = llamaSwapBaseURL(options.baseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL);
|
|
10818
|
+
const models = await readLlamaSwapCatalog(baseURL, options.fetch);
|
|
10819
|
+
try {
|
|
10820
|
+
return withLoadedState(models, await readLlamaSwapResidency(baseURL, options.fetch));
|
|
10821
|
+
} catch (error) {
|
|
10822
|
+
if (error instanceof ModelResidencyError && (error.code === "identity" || error.code === "unknown")) {
|
|
10823
|
+
return models;
|
|
10824
|
+
}
|
|
10825
|
+
throw error;
|
|
10826
|
+
}
|
|
10827
|
+
};
|
|
10828
|
+
return {
|
|
10829
|
+
id: "llama-swap",
|
|
10830
|
+
defaultBaseURL: LLAMA_SWAP_DEFAULT_BASE_URL,
|
|
10831
|
+
listModels,
|
|
10832
|
+
async probe(options) {
|
|
10833
|
+
const baseURL = llamaSwapBaseURL(options.baseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL);
|
|
10834
|
+
try {
|
|
10835
|
+
return {
|
|
10836
|
+
id: "llama-swap",
|
|
10837
|
+
baseURL,
|
|
10838
|
+
reachable: true,
|
|
10839
|
+
models: await listModels({ ...options, baseURL }),
|
|
10840
|
+
probedAt: options.probedAt
|
|
10841
|
+
};
|
|
10842
|
+
} catch (error) {
|
|
10843
|
+
return unavailableSnapshot({
|
|
10844
|
+
id: "llama-swap",
|
|
10845
|
+
baseURL,
|
|
10846
|
+
probedAt: options.probedAt,
|
|
10847
|
+
error
|
|
10848
|
+
});
|
|
10849
|
+
}
|
|
10850
|
+
}
|
|
10851
|
+
};
|
|
10852
|
+
}
|
|
10853
|
+
|
|
10457
10854
|
// src/local/lmstudio.ts
|
|
10458
10855
|
var LMSTUDIO_DEFAULT_BASE_URL = "http://localhost:1234/v1";
|
|
10459
10856
|
function createLMStudioAdapter() {
|
|
@@ -10506,6 +10903,7 @@ class RuntimeRegistry {
|
|
|
10506
10903
|
lmstudio: createLMStudioAdapter(),
|
|
10507
10904
|
"foundry-local": createFoundryLocalAdapter(),
|
|
10508
10905
|
lemonade: createLemonadeAdapter(),
|
|
10906
|
+
"llama-swap": createLlamaSwapAdapter(),
|
|
10509
10907
|
...options.adapters
|
|
10510
10908
|
};
|
|
10511
10909
|
this.clock = options.clock;
|
|
@@ -10635,6 +11033,28 @@ function withReportedModelSlots(runtime, reported, aliases) {
|
|
|
10635
11033
|
};
|
|
10636
11034
|
}
|
|
10637
11035
|
|
|
11036
|
+
// src/messages/nativeHost.ts
|
|
11037
|
+
var nativeHostMessages = {
|
|
11038
|
+
invalidNativeConfig: "The existing native opencode plugins/providers configuration cannot be safely merged. Correct its object and plugin declaration shapes before running setup.",
|
|
11039
|
+
setupPolicyQuestion: (paths, providers) => `Remove exact project-local provider.use deny rules for ${providers.join(", ")} from ${paths.join(", ")}? Older setup added these rules, but they may also be operator limits. Confirm only if you intend to enable the guarded native transport.`,
|
|
11040
|
+
setupPolicyPreserved: "Setup stopped. Provider-denial rules were preserved and no files were written.",
|
|
11041
|
+
setupPolicyNotice: "Only the explicitly reviewed project rules were removed. Wildcard, scoped, unrelated and global provider denials remain authoritative.",
|
|
11042
|
+
unsupportedVersion: (version) => `openteam has not verified the native plugin contract for opencode ${version}. The supported native host is 2.0.5.`,
|
|
11043
|
+
missingBindings: "The native opencode execution context is unavailable. No child session or privileged operation was started.",
|
|
11044
|
+
missingVersion: "The opencode host did not provide an authoritative version or server endpoint.",
|
|
11045
|
+
workspaceMismatch: "The native opencode session does not belong to this workspace.",
|
|
11046
|
+
noCompletedResponse: "The native opencode session stopped without a completed assistant response.",
|
|
11047
|
+
executionFailed: "The native opencode session failed.",
|
|
11048
|
+
executionInterrupted: "The native opencode session was interrupted.",
|
|
11049
|
+
unknownAgent: "The native opencode session has no available agent.",
|
|
11050
|
+
noAvailableModel: "The native opencode host has no model available in the configured execution domain.",
|
|
11051
|
+
executionDomainBlocked: "The native opencode request is outside the configured execution domain.",
|
|
11052
|
+
transportUnavailable: "This local runtime requires the guarded @ai-sdk/openai-compatible transport. Native and WebSocket transports are not admitted.",
|
|
11053
|
+
missingAdmission: "The native local request has no matching model authorization. No inference was sent.",
|
|
11054
|
+
toolFailed: "The openteam tool failed in the native opencode host.",
|
|
11055
|
+
eventCaptureFailed: "Native opencode event capture failed. The console may be missing activity; reload the plugin after resolving the host error."
|
|
11056
|
+
};
|
|
11057
|
+
|
|
10638
11058
|
// src/messages/orchestration.ts
|
|
10639
11059
|
var HOST_SESSION_REQUIRED = "Orchestration requires a nonempty host ToolContext sessionID.";
|
|
10640
11060
|
var PARENT_OVERRIDE_IGNORED = "parentSessionID override ignored: the trusted host ToolContext session owns this orchestration and its children. Programmatic callers must supply their parent as the ToolContext sessionID.";
|
|
@@ -10972,7 +11392,7 @@ var TRUNCATED_REASON_SUFFIX = "…[truncated]";
|
|
|
10972
11392
|
var MAX_SERIALIZED_ARRAY_ITEMS = 20;
|
|
10973
11393
|
var MAX_SERIALIZED_OBJECT_PROPERTIES = 25;
|
|
10974
11394
|
var MAX_SERIALIZED_DEPTH = 4;
|
|
10975
|
-
function
|
|
11395
|
+
function isObject4(value) {
|
|
10976
11396
|
return typeof value === "object" && value !== null;
|
|
10977
11397
|
}
|
|
10978
11398
|
function isError(value) {
|
|
@@ -11002,7 +11422,7 @@ function stringProperty(value, key) {
|
|
|
11002
11422
|
}
|
|
11003
11423
|
function nestedMessage(value, key) {
|
|
11004
11424
|
const container = safeGet(value, key);
|
|
11005
|
-
if (!
|
|
11425
|
+
if (!isObject4(container)) {
|
|
11006
11426
|
return;
|
|
11007
11427
|
}
|
|
11008
11428
|
const message = stringProperty(container, "message");
|
|
@@ -11086,7 +11506,7 @@ function errorReason(error) {
|
|
|
11086
11506
|
return message;
|
|
11087
11507
|
}
|
|
11088
11508
|
}
|
|
11089
|
-
if (
|
|
11509
|
+
if (isObject4(error)) {
|
|
11090
11510
|
const message = stringProperty(error, "message");
|
|
11091
11511
|
if (message !== undefined) {
|
|
11092
11512
|
return message;
|
|
@@ -11104,7 +11524,7 @@ function errorReason(error) {
|
|
|
11104
11524
|
return safeToString(error);
|
|
11105
11525
|
}
|
|
11106
11526
|
function isNormalizedSessionFailure(value) {
|
|
11107
|
-
return
|
|
11527
|
+
return isObject4(value) && safeGet(value, "kind") === "normalized-session-failure" && typeof safeGet(value, "failureClass") === "string" && typeof safeGet(value, "retryable") === "boolean";
|
|
11108
11528
|
}
|
|
11109
11529
|
function createArgs(req) {
|
|
11110
11530
|
const body = {};
|
|
@@ -11231,7 +11651,7 @@ async function confirmSessionStopped(client, sessionID, directory, deps, deadlin
|
|
|
11231
11651
|
return { confirmed: false, cause: "status-error" };
|
|
11232
11652
|
}
|
|
11233
11653
|
const statuses = statusOutcome.value;
|
|
11234
|
-
if (!
|
|
11654
|
+
if (!isObject4(statuses)) {
|
|
11235
11655
|
return { confirmed: false, cause: "status-error" };
|
|
11236
11656
|
}
|
|
11237
11657
|
if (safeGet(statuses, "error") !== undefined) {
|
|
@@ -11381,8 +11801,11 @@ async function runSubsession(client, req, deps) {
|
|
|
11381
11801
|
}
|
|
11382
11802
|
|
|
11383
11803
|
// src/local/modelResidency.ts
|
|
11804
|
+
function runtimeRequiresModelResidency(kind) {
|
|
11805
|
+
return kind === "lemonade" || kind === "llama-swap";
|
|
11806
|
+
}
|
|
11384
11807
|
function hasRequiredModelResidency(kind, model) {
|
|
11385
|
-
return kind
|
|
11808
|
+
return !runtimeRequiresModelResidency(kind) || model?.loaded === true;
|
|
11386
11809
|
}
|
|
11387
11810
|
|
|
11388
11811
|
// src/plugin/availability.ts
|
|
@@ -11391,9 +11814,9 @@ function modelKey2(model) {
|
|
|
11391
11814
|
}
|
|
11392
11815
|
function availabilityMetadata({
|
|
11393
11816
|
apiModelID: _apiModelID,
|
|
11394
|
-
...
|
|
11817
|
+
...metadata3
|
|
11395
11818
|
}) {
|
|
11396
|
-
return
|
|
11819
|
+
return metadata3;
|
|
11397
11820
|
}
|
|
11398
11821
|
function compareAvailableModels(left, right) {
|
|
11399
11822
|
const kindOrder = Number(left.kind === "frontier") - Number(right.kind === "frontier");
|
|
@@ -11413,7 +11836,7 @@ function isConfiguredLocalModel(config, model) {
|
|
|
11413
11836
|
return config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === model.providerID);
|
|
11414
11837
|
}
|
|
11415
11838
|
function requiresModelResidency(config, model) {
|
|
11416
|
-
return config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === model.providerID &&
|
|
11839
|
+
return config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === model.providerID && runtimeRequiresModelResidency(localRuntimeKind(runtime)));
|
|
11417
11840
|
}
|
|
11418
11841
|
function configuredFrontierProviderIDs(config) {
|
|
11419
11842
|
const providerIDs = new Set([config.baseline.hardDefault.providerID]);
|
|
@@ -11612,6 +12035,10 @@ function createAvailabilityCache(options) {
|
|
|
11612
12035
|
ref: { ...profile.ref },
|
|
11613
12036
|
costPer1M: { ...profile.costPer1M }
|
|
11614
12037
|
})),
|
|
12038
|
+
waitForRefresh: async () => {
|
|
12039
|
+
while (inFlight !== undefined)
|
|
12040
|
+
await inFlight;
|
|
12041
|
+
},
|
|
11615
12042
|
refresh: () => {
|
|
11616
12043
|
if (inFlight === undefined) {
|
|
11617
12044
|
cached = cached.map((model) => requiresModelResidency(options.config, model) ? { ...model, available: false } : model);
|
|
@@ -12039,6 +12466,7 @@ var PROVIDER_LABELS = {
|
|
|
12039
12466
|
ollama: "Ollama",
|
|
12040
12467
|
lmstudio: "LM Studio",
|
|
12041
12468
|
lemonade: "Lemonade Server",
|
|
12469
|
+
"llama-swap": executionSetupMessages.llamaSwap.label,
|
|
12042
12470
|
"foundry-local": "Foundry Local"
|
|
12043
12471
|
};
|
|
12044
12472
|
function providerLabel(providerID) {
|
|
@@ -12260,7 +12688,8 @@ var KNOWN_LOCAL_PROVIDER_IDS = new Set([
|
|
|
12260
12688
|
"ollama",
|
|
12261
12689
|
"lmstudio",
|
|
12262
12690
|
"foundry-local",
|
|
12263
|
-
"lemonade"
|
|
12691
|
+
"lemonade",
|
|
12692
|
+
"llama-swap"
|
|
12264
12693
|
]);
|
|
12265
12694
|
var KNOWN_FRONTIER_PROVIDER_IDS = new Set([
|
|
12266
12695
|
"github-copilot",
|
|
@@ -12606,7 +13035,7 @@ function runtimeLine(snapshot, runtime) {
|
|
|
12606
13035
|
const mark = snapshot.reachable ? "✓" : "✗";
|
|
12607
13036
|
const detail = snapshot.reachable ? `${snapshot.models.length} model(s)` : snapshot.error ?? "unreachable";
|
|
12608
13037
|
const declared = runtime?.maxConcurrency;
|
|
12609
|
-
const slots = declared === undefined ?
|
|
13038
|
+
const slots = declared === undefined ? doctorMessages.defaultRuntimeSlots(effectiveMaxConcurrency(runtime ?? {})) : ` · ${declared} slot(s) (declared)`;
|
|
12610
13039
|
return ` ${mark} ${snapshot.id.padEnd(14)} ${snapshot.baseURL || "(no baseURL)"} — ${detail}${slots}`;
|
|
12611
13040
|
}
|
|
12612
13041
|
function runtimeModelSlotsLine(runtime, snapshot) {
|
|
@@ -12618,6 +13047,8 @@ function runtimeModelSlotsLine(runtime, snapshot) {
|
|
|
12618
13047
|
if (runtime === undefined || snapshot === undefined || !snapshot.reachable || snapshot.models.length === 0) {
|
|
12619
13048
|
return [];
|
|
12620
13049
|
}
|
|
13050
|
+
if (localRuntimeKind(runtime) === "llama-swap")
|
|
13051
|
+
return [doctorMessages.llamaSwapConcurrency];
|
|
12621
13052
|
const reported = snapshot.models.flatMap((model) => model.slots === undefined ? [] : [`${model.modelID} ${model.slots} slot(s)`]);
|
|
12622
13053
|
if (reported.length > 0) {
|
|
12623
13054
|
return [
|
|
@@ -13021,7 +13452,7 @@ function renderDoctor(input) {
|
|
|
13021
13452
|
continue;
|
|
13022
13453
|
}
|
|
13023
13454
|
const runtime = enabledRuntimes.find((r) => r.id === snapshot.id);
|
|
13024
|
-
const cap = runtime
|
|
13455
|
+
const cap = effectiveMaxConcurrency(runtime ?? {});
|
|
13025
13456
|
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.");
|
|
13026
13457
|
}
|
|
13027
13458
|
lines.push(` telemetry: ${input.telemetryPath} — ${input.telemetryRecords} record(s)`);
|
|
@@ -13442,6 +13873,16 @@ function renderMigration(report) {
|
|
|
13442
13873
|
// src/commands/purge.ts
|
|
13443
13874
|
import * as path3 from "node:path";
|
|
13444
13875
|
|
|
13876
|
+
// src/config/fieldProjection.ts
|
|
13877
|
+
function only(keys) {
|
|
13878
|
+
return (value) => {
|
|
13879
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
13880
|
+
return value;
|
|
13881
|
+
const record = value;
|
|
13882
|
+
return Object.fromEntries(keys.filter((key) => Object.hasOwn(record, key)).map((key) => [key, record[key]]));
|
|
13883
|
+
};
|
|
13884
|
+
}
|
|
13885
|
+
|
|
13445
13886
|
// src/storage/graph/soakRecorder.ts
|
|
13446
13887
|
var SOAK_EVIDENCE_DIR = ".opencode/soak";
|
|
13447
13888
|
var SOAK_RECORDER_BASE_DIR = ".opencode";
|
|
@@ -13633,6 +14074,38 @@ function missingGeneratedStateRules(gitignore) {
|
|
|
13633
14074
|
const present = new Set(activeGitignoreRules(gitignore));
|
|
13634
14075
|
return OPENCODE_GITIGNORE_GENERATED_STATE_RULES.filter((rule) => !present.has(rule));
|
|
13635
14076
|
}
|
|
14077
|
+
var KNOWN_RUNTIMES = [
|
|
14078
|
+
{
|
|
14079
|
+
id: "ollama",
|
|
14080
|
+
label: "Ollama",
|
|
14081
|
+
defaultBaseURL: "http://localhost:11434/v1",
|
|
14082
|
+
fallbackModelID: "qwen3:8b"
|
|
14083
|
+
},
|
|
14084
|
+
{
|
|
14085
|
+
id: "lmstudio",
|
|
14086
|
+
label: "LM Studio",
|
|
14087
|
+
defaultBaseURL: "http://localhost:1234/v1",
|
|
14088
|
+
fallbackModelID: "qwen2.5-coder"
|
|
14089
|
+
},
|
|
14090
|
+
{
|
|
14091
|
+
id: "lemonade",
|
|
14092
|
+
label: "Lemonade Server",
|
|
14093
|
+
defaultBaseURL: LEMONADE_DEFAULT_BASE_URL,
|
|
14094
|
+
fallbackModelID: "Qwen3-Coder-30B-A3B-Instruct-GGUF"
|
|
14095
|
+
},
|
|
14096
|
+
{
|
|
14097
|
+
id: "llama-swap",
|
|
14098
|
+
label: executionSetupMessages.llamaSwap.label,
|
|
14099
|
+
defaultBaseURL: LLAMA_SWAP_DEFAULT_BASE_URL,
|
|
14100
|
+
fallbackModelID: ""
|
|
14101
|
+
},
|
|
14102
|
+
{
|
|
14103
|
+
id: "foundry-local",
|
|
14104
|
+
label: "Foundry Local",
|
|
14105
|
+
fallbackModelID: "Phi-4-mini-instruct",
|
|
14106
|
+
dynamicPort: true
|
|
14107
|
+
}
|
|
14108
|
+
];
|
|
13636
14109
|
|
|
13637
14110
|
// src/commands/purge.ts
|
|
13638
14111
|
var PURGE_CATEGORY_IDS = [
|
|
@@ -14661,7 +15134,7 @@ function liveModelsFromSnapshots(snapshots, config) {
|
|
|
14661
15134
|
map.set(runtime.defaultModel.providerID, {
|
|
14662
15135
|
reachable: snapshot.reachable,
|
|
14663
15136
|
modelIDs: new Set(snapshot.models.filter((model) => hasRequiredModelResidency(kind, model)).map((model) => model.modelID)),
|
|
14664
|
-
...kind
|
|
15137
|
+
...runtimeRequiresModelResidency(kind) ? {
|
|
14665
15138
|
residency: new Map(snapshot.models.map((model) => [model.modelID, model.loaded]))
|
|
14666
15139
|
} : {}
|
|
14667
15140
|
});
|
|
@@ -14991,9 +15464,9 @@ async function runCli(argv, deps) {
|
|
|
14991
15464
|
const inventoryNotes = [];
|
|
14992
15465
|
const readySnapshots = snapshots.map((snapshot) => {
|
|
14993
15466
|
const runtime = config.local.runtimes.find((runtime2) => runtime2.id === snapshot.id);
|
|
14994
|
-
if (runtime === undefined || localRuntimeKind(runtime)
|
|
15467
|
+
if (runtime === undefined || !runtimeRequiresModelResidency(localRuntimeKind(runtime)))
|
|
14995
15468
|
return snapshot;
|
|
14996
|
-
inventoryNotes.push(executionSetupMessages.lemonadeInventory(runtime.id, runtime.defaultModel.providerID, snapshot.models));
|
|
15469
|
+
inventoryNotes.push(executionSetupMessages.lemonadeInventory(runtime.id, runtime.defaultModel.providerID, snapshot.models, localRuntimeKind(runtime) === "llama-swap" ? "llama-swap" : "Lemonade"));
|
|
14997
15470
|
return {
|
|
14998
15471
|
...snapshot,
|
|
14999
15472
|
models: snapshot.models.filter((model) => hasRequiredModelResidency(localRuntimeKind(runtime), model))
|
|
@@ -15004,7 +15477,7 @@ async function runCli(argv, deps) {
|
|
|
15004
15477
|
if (agent.model === undefined || live !== "not-loaded" && live !== "residency-unknown")
|
|
15005
15478
|
return [];
|
|
15006
15479
|
return [
|
|
15007
|
-
executionSetupMessages.lemonadeAgentResidency(agent.name, `${agent.model.providerID}/${agent.model.modelID}`, live)
|
|
15480
|
+
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")
|
|
15008
15481
|
];
|
|
15009
15482
|
});
|
|
15010
15483
|
return {
|
|
@@ -15204,7 +15677,7 @@ function createCommandTool(deps) {
|
|
|
15204
15677
|
// src/plugin/consoleTool.ts
|
|
15205
15678
|
import { spawn as nodeSpawn } from "node:child_process";
|
|
15206
15679
|
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
15207
|
-
import { mkdir as mkdir4, open as open3 } from "node:fs/promises";
|
|
15680
|
+
import { mkdir as mkdir4, open as open3, stat as stat3 } from "node:fs/promises";
|
|
15208
15681
|
import { dirname as dirname6, join as join13, resolve as resolve8 } from "node:path";
|
|
15209
15682
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
15210
15683
|
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
@@ -15214,8 +15687,8 @@ import {
|
|
|
15214
15687
|
mkdir as mkdir3,
|
|
15215
15688
|
open as open2,
|
|
15216
15689
|
readFile as readFile4,
|
|
15217
|
-
|
|
15218
|
-
|
|
15690
|
+
rename as rename2,
|
|
15691
|
+
unlink
|
|
15219
15692
|
} from "node:fs/promises";
|
|
15220
15693
|
import { dirname as dirname5, join as join12, resolve as resolve7 } from "node:path";
|
|
15221
15694
|
import { z as z22 } from "zod";
|
|
@@ -15515,10 +15988,17 @@ async function acquireLaunchLockOrWait(workspaceRoot, paths, launchId, deps) {
|
|
|
15515
15988
|
currentLock = await readConsoleLaunchLock(paths.launchLockPath);
|
|
15516
15989
|
} catch (error) {
|
|
15517
15990
|
if (error instanceof ConsoleRuntimeStateFileError) {
|
|
15518
|
-
await
|
|
15519
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
15991
|
+
const info = await stat3(paths.launchLockPath).catch((error2) => {
|
|
15992
|
+
if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT")
|
|
15993
|
+
return;
|
|
15994
|
+
throw error2;
|
|
15995
|
+
});
|
|
15996
|
+
if (info === undefined || deps.now() - info.mtimeMs > LAUNCH_LOCK_STALE_MS) {
|
|
15997
|
+
await removeConsoleLaunchLock(paths.launchLockPath);
|
|
15998
|
+
const retry = await acquireConsoleLaunchLock(paths.launchLockPath, attemptLock);
|
|
15999
|
+
if (retry !== "busy") {
|
|
16000
|
+
return { kind: "acquired", release: retry.release };
|
|
16001
|
+
}
|
|
15522
16002
|
}
|
|
15523
16003
|
} else {
|
|
15524
16004
|
throw error;
|
|
@@ -15591,7 +16071,8 @@ async function spawnDetachedConsole(workspaceRoot, paths, launchId, deps) {
|
|
|
15591
16071
|
], {
|
|
15592
16072
|
cwd: workspaceRoot,
|
|
15593
16073
|
detached: true,
|
|
15594
|
-
stdio: ["ignore", logHandle.fd, logHandle.fd]
|
|
16074
|
+
stdio: ["ignore", logHandle.fd, logHandle.fd],
|
|
16075
|
+
...deps.environment === undefined ? {} : { env: deps.environment() }
|
|
15595
16076
|
});
|
|
15596
16077
|
child.unref?.();
|
|
15597
16078
|
return child;
|
|
@@ -15626,7 +16107,8 @@ function createConsoleTool(deps = {}) {
|
|
|
15626
16107
|
sendSignal,
|
|
15627
16108
|
sleep,
|
|
15628
16109
|
resolveCliEntrypoint,
|
|
15629
|
-
spawn
|
|
16110
|
+
spawn,
|
|
16111
|
+
...deps.environment === undefined ? {} : { environment: deps.environment }
|
|
15630
16112
|
};
|
|
15631
16113
|
if (args.action === "status") {
|
|
15632
16114
|
return summaryFromProbe(await readRuntimeProbe(workspaceRoot, {
|
|
@@ -15744,10 +16226,7 @@ function createConsoleTool(deps = {}) {
|
|
|
15744
16226
|
});
|
|
15745
16227
|
}
|
|
15746
16228
|
await removeConsoleRuntimeState(paths.runtimeStatePath);
|
|
15747
|
-
const child = await spawnDetachedConsole(workspaceRoot, paths, launchId,
|
|
15748
|
-
resolveCliEntrypoint: runtimeDeps.resolveCliEntrypoint,
|
|
15749
|
-
spawn: runtimeDeps.spawn
|
|
15750
|
-
});
|
|
16229
|
+
const child = await spawnDetachedConsole(workspaceRoot, paths, launchId, runtimeDeps);
|
|
15751
16230
|
const probe = await waitForHealthyRuntime(workspaceRoot, START_TIMEOUT_MS, {
|
|
15752
16231
|
fetch: runtimeDeps.fetch,
|
|
15753
16232
|
processExists: runtimeDeps.processExists,
|
|
@@ -17234,6 +17713,84 @@ function createLemonadeGuardedFetch(options) {
|
|
|
17234
17713
|
});
|
|
17235
17714
|
}
|
|
17236
17715
|
|
|
17716
|
+
// src/local/llamaSwapRequestGuard.ts
|
|
17717
|
+
var inferencePaths2 = new Set([
|
|
17718
|
+
"/chat/completions",
|
|
17719
|
+
"/completions",
|
|
17720
|
+
"/embeddings",
|
|
17721
|
+
"/responses"
|
|
17722
|
+
]);
|
|
17723
|
+
async function validatedModelID(payload, modelIDs) {
|
|
17724
|
+
if (typeof payload !== "object" || payload === null || !("model" in payload) || typeof payload.model !== "string" || payload.model.length === 0 || payload.model !== payload.model.trim()) {
|
|
17725
|
+
throw new ModelResidencyError("request", "llama-swap");
|
|
17726
|
+
}
|
|
17727
|
+
if (modelIDs === undefined) {
|
|
17728
|
+
return payload.model;
|
|
17729
|
+
}
|
|
17730
|
+
const ids = typeof modelIDs === "function" ? await modelIDs() : modelIDs;
|
|
17731
|
+
if (ids.has(payload.model)) {
|
|
17732
|
+
return payload.model;
|
|
17733
|
+
}
|
|
17734
|
+
throw new ModelResidencyError("identity", "llama-swap");
|
|
17735
|
+
}
|
|
17736
|
+
function createLlamaSwapGuardedFetch(options) {
|
|
17737
|
+
const base = llamaSwapBaseURL(options.baseURL);
|
|
17738
|
+
const upstream = options.fetch;
|
|
17739
|
+
const healthFetch = options.healthFetch ?? upstream;
|
|
17740
|
+
return Object.assign(async (input, init) => {
|
|
17741
|
+
const request = new Request(input, init);
|
|
17742
|
+
const url = new URL(request.url);
|
|
17743
|
+
if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !request.url.startsWith(`${base}/`)) {
|
|
17744
|
+
throw new ModelResidencyError("destination", "llama-swap");
|
|
17745
|
+
}
|
|
17746
|
+
const path4 = request.url.slice(base.length);
|
|
17747
|
+
const metadataOnly = request.method === "GET" && /^\/models(?:\/[^/]+)?$/.test(path4);
|
|
17748
|
+
if (!metadataOnly && (request.method !== "POST" || !inferencePaths2.has(path4))) {
|
|
17749
|
+
throw new ModelResidencyError("request", "llama-swap");
|
|
17750
|
+
}
|
|
17751
|
+
if (!metadataOnly) {
|
|
17752
|
+
await withResidencyDeadline(async (signal) => {
|
|
17753
|
+
signal.throwIfAborted();
|
|
17754
|
+
let payload;
|
|
17755
|
+
try {
|
|
17756
|
+
payload = await request.clone().json();
|
|
17757
|
+
} catch {
|
|
17758
|
+
signal.throwIfAborted();
|
|
17759
|
+
throw new ModelResidencyError("request", "llama-swap");
|
|
17760
|
+
}
|
|
17761
|
+
signal.throwIfAborted();
|
|
17762
|
+
const model = await validatedModelID(payload, options.modelIDs);
|
|
17763
|
+
const residency = await readLlamaSwapResidency(base, healthFetch, {
|
|
17764
|
+
...options,
|
|
17765
|
+
signal,
|
|
17766
|
+
headers: request.headers
|
|
17767
|
+
});
|
|
17768
|
+
signal.throwIfAborted();
|
|
17769
|
+
if (!residency.has(model)) {
|
|
17770
|
+
throw new ModelResidencyError("identity", "llama-swap");
|
|
17771
|
+
}
|
|
17772
|
+
if (residency.get(model)?.loaded !== true) {
|
|
17773
|
+
throw new ModelResidencyError("inactive", "llama-swap");
|
|
17774
|
+
}
|
|
17775
|
+
}, { ...options, signal: request.signal, runtimeLabel: "llama-swap" });
|
|
17776
|
+
}
|
|
17777
|
+
if (request.signal.aborted) {
|
|
17778
|
+
throw new ModelResidencyError("cancelled", "llama-swap");
|
|
17779
|
+
}
|
|
17780
|
+
const {
|
|
17781
|
+
body: _body,
|
|
17782
|
+
headers: _headers,
|
|
17783
|
+
method: _method,
|
|
17784
|
+
signal: _signal,
|
|
17785
|
+
redirect: _redirect,
|
|
17786
|
+
...transportOptions
|
|
17787
|
+
} = init ?? {};
|
|
17788
|
+
return upstream(request, { ...transportOptions, redirect: "error" });
|
|
17789
|
+
}, {
|
|
17790
|
+
preconnect: (...args) => upstream.preconnect?.(...args)
|
|
17791
|
+
});
|
|
17792
|
+
}
|
|
17793
|
+
|
|
17237
17794
|
// src/plugin/modelResidency.ts
|
|
17238
17795
|
function isFetch(value) {
|
|
17239
17796
|
return typeof value === "function";
|
|
@@ -17273,20 +17830,29 @@ function providerRoutes(provider) {
|
|
|
17273
17830
|
npm: model?.provider?.npm ?? provider.npm
|
|
17274
17831
|
}));
|
|
17275
17832
|
}
|
|
17276
|
-
function wireID(key, model) {
|
|
17833
|
+
function wireID(key, model, runtimeLabel = "Lemonade") {
|
|
17277
17834
|
const id = model?.id ?? key;
|
|
17278
17835
|
if (id.length === 0 || id !== id.trim())
|
|
17279
|
-
throw new ModelResidencyError("identity");
|
|
17836
|
+
throw new ModelResidencyError("identity", runtimeLabel);
|
|
17280
17837
|
return id;
|
|
17281
17838
|
}
|
|
17282
17839
|
function nativeEligibleID(id) {
|
|
17283
17840
|
return id === "openai" || id === "anthropic" || id.startsWith("opencode");
|
|
17284
17841
|
}
|
|
17842
|
+
function normalizedBase(kind, baseURL) {
|
|
17843
|
+
return kind === "llama-swap" ? llamaSwapBaseURL(baseURL) : lemonadeBaseURL(baseURL);
|
|
17844
|
+
}
|
|
17845
|
+
function runtimeBase(runtime) {
|
|
17846
|
+
return runtime.baseURL ?? (localRuntimeKind(runtime) === "llama-swap" ? LLAMA_SWAP_DEFAULT_BASE_URL : LEMONADE_DEFAULT_BASE_URL);
|
|
17847
|
+
}
|
|
17848
|
+
function residencyRuntimeLabel(config, providerID) {
|
|
17849
|
+
return config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === providerID && localRuntimeKind(runtime) === "llama-swap") ? "llama-swap" : "Lemonade";
|
|
17850
|
+
}
|
|
17285
17851
|
function configuredRuntime(config, providerID) {
|
|
17286
17852
|
const runtimes = config.local.runtimes.filter((runtime2) => runtime2.defaultModel.providerID === providerID);
|
|
17287
17853
|
const runtime = runtimes[0];
|
|
17288
17854
|
if (runtimes.length !== 1 || runtime === undefined || !runtime.enabled) {
|
|
17289
|
-
throw new ModelResidencyError("destination");
|
|
17855
|
+
throw new ModelResidencyError("destination", residencyRuntimeLabel(config, providerID));
|
|
17290
17856
|
}
|
|
17291
17857
|
return runtime;
|
|
17292
17858
|
}
|
|
@@ -17295,8 +17861,9 @@ function createModelResidencyGuard(config, options) {
|
|
|
17295
17861
|
const blocked = new Set;
|
|
17296
17862
|
let hostConfig;
|
|
17297
17863
|
const readEnv = options.readEnv ?? ((name) => process.env[name]);
|
|
17298
|
-
const
|
|
17299
|
-
const
|
|
17864
|
+
const failure = (code, providerID) => new ModelResidencyError(code, residencyRuntimeLabel(config, providerID));
|
|
17865
|
+
const residentRuntimes = () => config.local.runtimes.filter((runtime) => runtimeRequiresModelResidency(localRuntimeKind(runtime)));
|
|
17866
|
+
const protects = (providerID, baseURL) => residentRuntimes().some((runtime) => {
|
|
17300
17867
|
if (runtime.defaultModel.providerID === providerID)
|
|
17301
17868
|
return true;
|
|
17302
17869
|
if (baseURL === undefined)
|
|
@@ -17304,13 +17871,13 @@ function createModelResidencyGuard(config, options) {
|
|
|
17304
17871
|
const binding = endpointBinding(baseURL, readEnv);
|
|
17305
17872
|
if (binding.ambiguous)
|
|
17306
17873
|
return true;
|
|
17307
|
-
return binding.origin !== undefined && binding.origin === origin(runtime
|
|
17874
|
+
return binding.origin !== undefined && binding.origin === origin(runtimeBase(runtime));
|
|
17308
17875
|
});
|
|
17309
17876
|
const resolvedWireID = (model, declaredID) => {
|
|
17310
17877
|
const id = options.resolveApiModelID === undefined ? declaredID : options.resolveApiModelID(model);
|
|
17311
17878
|
if (id === undefined)
|
|
17312
|
-
throw
|
|
17313
|
-
return wireID(id, undefined);
|
|
17879
|
+
throw failure("identity", model.providerID);
|
|
17880
|
+
return wireID(id, undefined, residencyRuntimeLabel(config, model.providerID));
|
|
17314
17881
|
};
|
|
17315
17882
|
const configHook = async (host) => {
|
|
17316
17883
|
hostConfig = host;
|
|
@@ -17330,21 +17897,22 @@ function createModelResidencyGuard(config, options) {
|
|
|
17330
17897
|
...new Set([...host.disabled_providers ?? [], providerID])
|
|
17331
17898
|
];
|
|
17332
17899
|
const reject = Object.assign(async () => {
|
|
17333
|
-
throw
|
|
17900
|
+
throw failure("transport", providerID);
|
|
17334
17901
|
}, { preconnect: () => {} });
|
|
17335
17902
|
provider.options = { ...provider.options, fetch: reject };
|
|
17336
17903
|
try {
|
|
17337
17904
|
const runtime = configuredRuntime(config, providerID);
|
|
17338
|
-
const
|
|
17339
|
-
|
|
17340
|
-
|
|
17905
|
+
const kind = localRuntimeKind(runtime);
|
|
17906
|
+
const baseURL = normalizedBase(kind, runtimeBase(runtime));
|
|
17907
|
+
if (!runtimeRequiresModelResidency(kind) || routes.some((route) => route.baseURL === undefined || normalizedBase(kind, route.baseURL) !== baseURL))
|
|
17908
|
+
throw failure("destination", providerID);
|
|
17341
17909
|
if (routes.some((route) => route.npm !== "@ai-sdk/openai-compatible") || nativeEligibleID(providerID) || !isFetch(originalFetch))
|
|
17342
|
-
throw
|
|
17910
|
+
throw failure("transport", providerID);
|
|
17343
17911
|
const modelKeys = new Map;
|
|
17344
17912
|
for (const [key, model] of Object.entries(provider.models ?? {})) {
|
|
17345
|
-
if (model.options?.fetch !== undefined || model.options?.baseURL !== undefined && (typeof model.options.baseURL !== "string" ||
|
|
17346
|
-
throw
|
|
17347
|
-
modelKeys.set(key, wireID(key, model));
|
|
17913
|
+
if (model.options?.fetch !== undefined || model.options?.baseURL !== undefined && (typeof model.options.baseURL !== "string" || normalizedBase(kind, model.options.baseURL) !== baseURL))
|
|
17914
|
+
throw failure("transport", providerID);
|
|
17915
|
+
modelKeys.set(key, wireID(key, model, residencyRuntimeLabel(config, providerID)));
|
|
17348
17916
|
}
|
|
17349
17917
|
if (!modelKeys.has(runtime.defaultModel.modelID))
|
|
17350
17918
|
modelKeys.set(runtime.defaultModel.modelID, undefined);
|
|
@@ -17353,11 +17921,12 @@ function createModelResidencyGuard(config, options) {
|
|
|
17353
17921
|
for (const [modelID2, declaredID] of modelKeys) {
|
|
17354
17922
|
const id = options.resolveApiModelID === undefined ? declaredID : options.resolveApiModelID({ providerID, modelID: modelID2 });
|
|
17355
17923
|
if (id !== undefined)
|
|
17356
|
-
ids.add(wireID(id, undefined));
|
|
17924
|
+
ids.add(wireID(id, undefined, residencyRuntimeLabel(config, providerID)));
|
|
17357
17925
|
}
|
|
17358
17926
|
return ids;
|
|
17359
17927
|
};
|
|
17360
|
-
const
|
|
17928
|
+
const guardFetch = kind === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch;
|
|
17929
|
+
const fetch = guardFetch({
|
|
17361
17930
|
...options,
|
|
17362
17931
|
baseURL,
|
|
17363
17932
|
modelIDs: async () => {
|
|
@@ -17368,7 +17937,10 @@ function createModelResidencyGuard(config, options) {
|
|
|
17368
17937
|
healthFetch: options.fetch
|
|
17369
17938
|
});
|
|
17370
17939
|
provider.options.fetch = fetch;
|
|
17940
|
+
if (kind === "llama-swap")
|
|
17941
|
+
provider.options.baseURL = baseURL;
|
|
17371
17942
|
guarded.set(providerID, {
|
|
17943
|
+
kind,
|
|
17372
17944
|
baseURL,
|
|
17373
17945
|
fetch,
|
|
17374
17946
|
originalFetch,
|
|
@@ -17382,7 +17954,7 @@ function createModelResidencyGuard(config, options) {
|
|
|
17382
17954
|
} catch (error) {
|
|
17383
17955
|
if (!(error instanceof ModelResidencyError))
|
|
17384
17956
|
throw error;
|
|
17385
|
-
(options.warn ?? console.warn)(modelResidencyMessages.providerDisabled(providerID));
|
|
17957
|
+
(options.warn ?? console.warn)(modelResidencyMessages.providerDisabled(providerID, residencyRuntimeLabel(config, providerID)));
|
|
17386
17958
|
}
|
|
17387
17959
|
}
|
|
17388
17960
|
};
|
|
@@ -17392,48 +17964,52 @@ function createModelResidencyGuard(config, options) {
|
|
|
17392
17964
|
return;
|
|
17393
17965
|
const provider = guarded.get(input.model.providerID);
|
|
17394
17966
|
if (provider === undefined || blocked.has(input.model.providerID) || input.provider.options.fetch !== provider.fetch || input.model.api.npm !== "@ai-sdk/openai-compatible" || nativeEligibleID(input.model.providerID))
|
|
17395
|
-
throw
|
|
17396
|
-
if (
|
|
17397
|
-
throw
|
|
17967
|
+
throw failure("transport", input.model.providerID);
|
|
17968
|
+
if (normalizedBase(provider.kind, base) !== provider.baseURL) {
|
|
17969
|
+
throw failure("destination", input.model.providerID);
|
|
17398
17970
|
}
|
|
17971
|
+
if (input.model.options.fetch !== undefined || input.model.options.baseURL !== undefined && (typeof input.model.options.baseURL !== "string" || normalizedBase(provider.kind, input.model.options.baseURL) !== provider.baseURL))
|
|
17972
|
+
throw failure("transport", input.model.providerID);
|
|
17399
17973
|
const id = resolvedWireID({ providerID: input.model.providerID, modelID: input.model.id }, provider.modelKeys.get(input.model.id));
|
|
17400
17974
|
if (id !== input.model.api.id)
|
|
17401
|
-
throw
|
|
17975
|
+
throw failure("identity", input.model.providerID);
|
|
17402
17976
|
if (!provider.modelKeys.has(input.model.id))
|
|
17403
17977
|
provider.modelKeys.set(input.model.id, undefined);
|
|
17404
17978
|
};
|
|
17405
17979
|
const localTarget = (model) => {
|
|
17406
17980
|
const runtime = configuredRuntime(config, model.providerID);
|
|
17981
|
+
const kind = localRuntimeKind(runtime);
|
|
17982
|
+
const needsResidency = runtimeRequiresModelResidency(kind);
|
|
17407
17983
|
if (hostConfig?.disabled_providers?.includes(model.providerID)) {
|
|
17408
|
-
throw
|
|
17984
|
+
throw failure("transport", model.providerID);
|
|
17409
17985
|
}
|
|
17410
17986
|
if (runtime.baseURL === undefined)
|
|
17411
|
-
throw
|
|
17987
|
+
throw failure("destination", model.providerID);
|
|
17412
17988
|
const provider = hostConfig?.provider?.[model.providerID];
|
|
17413
17989
|
const entry = Object.hasOwn(provider?.models ?? {}, model.modelID) ? provider?.models?.[model.modelID] : undefined;
|
|
17414
17990
|
const configuredURL = provider === undefined ? undefined : providerBase(provider, entry);
|
|
17415
|
-
if (configuredURL !== undefined && new URL(configuredURL).href.replace(/\/$/, "") !== new URL(runtime.baseURL).href.replace(/\/$/, "")) {
|
|
17416
|
-
throw
|
|
17991
|
+
if (configuredURL !== undefined && (needsResidency ? normalizedBase(kind, configuredURL) !== normalizedBase(kind, runtime.baseURL) : new URL(configuredURL).href.replace(/\/$/, "") !== new URL(runtime.baseURL).href.replace(/\/$/, ""))) {
|
|
17992
|
+
throw failure("destination", model.providerID);
|
|
17417
17993
|
}
|
|
17418
|
-
const modelID2 = resolvedWireID(model, entry === undefined &&
|
|
17994
|
+
const modelID2 = resolvedWireID(model, entry === undefined && needsResidency ? undefined : wireID(model.modelID, entry, residencyRuntimeLabel(config, model.providerID)));
|
|
17419
17995
|
let fetch = options.fetch;
|
|
17420
|
-
if (
|
|
17996
|
+
if (needsResidency) {
|
|
17421
17997
|
const installed = guarded.get(model.providerID);
|
|
17422
17998
|
if (installed === undefined || blocked.has(model.providerID)) {
|
|
17423
|
-
throw
|
|
17999
|
+
throw failure("transport", model.providerID);
|
|
17424
18000
|
}
|
|
17425
|
-
if (installed.baseURL !==
|
|
17426
|
-
throw
|
|
18001
|
+
if (installed.baseURL !== normalizedBase(kind, runtime.baseURL)) {
|
|
18002
|
+
throw failure("destination", model.providerID);
|
|
17427
18003
|
}
|
|
17428
18004
|
if (!installed.modelKeys.has(model.modelID))
|
|
17429
18005
|
installed.modelKeys.set(model.modelID, undefined);
|
|
17430
18006
|
if (!installed.modelIDs().has(modelID2))
|
|
17431
|
-
throw
|
|
18007
|
+
throw failure("identity", model.providerID);
|
|
17432
18008
|
fetch = installed.fetch;
|
|
17433
18009
|
}
|
|
17434
18010
|
const configuredHeaders = z25.record(z25.string(), z25.string()).safeParse(provider?.options?.headers ?? {});
|
|
17435
18011
|
if (!configuredHeaders.success)
|
|
17436
|
-
throw
|
|
18012
|
+
throw failure("configuration", model.providerID);
|
|
17437
18013
|
const headers = new Headers(configuredHeaders.data);
|
|
17438
18014
|
if (typeof provider?.options?.apiKey === "string" && !headers.has("authorization")) {
|
|
17439
18015
|
headers.set("authorization", `Bearer ${provider.options.apiKey}`);
|
|
@@ -17442,7 +18018,7 @@ function createModelResidencyGuard(config, options) {
|
|
|
17442
18018
|
headers.set(name, value);
|
|
17443
18019
|
const upstream = fetch;
|
|
17444
18020
|
return {
|
|
17445
|
-
baseURL: runtime.baseURL,
|
|
18021
|
+
baseURL: needsResidency ? normalizedBase(kind, runtime.baseURL) : runtime.baseURL,
|
|
17446
18022
|
modelID: modelID2,
|
|
17447
18023
|
fetch: [...headers].length === 0 ? fetch : Object.assign(async (input, init) => {
|
|
17448
18024
|
const request = new Request(input, init);
|
|
@@ -17549,8 +18125,8 @@ function createChatMessageHook(config, getAvailable, options = {}) {
|
|
|
17549
18125
|
await injectMemoryBestEffort(options.injectMemory, prompt, output);
|
|
17550
18126
|
return;
|
|
17551
18127
|
}
|
|
17552
|
-
await options.beforeRoute?.();
|
|
17553
18128
|
const role = await roleForAgentName(config, input.agent ?? output.message.agent, options.resolveAgentRole);
|
|
18129
|
+
await options.beforeRoute?.();
|
|
17554
18130
|
const derivedSignals = deriveTaskSignals(input, output);
|
|
17555
18131
|
const signals = role === undefined ? derivedSignals : withoutExplicitOverride(derivedSignals);
|
|
17556
18132
|
const executionPolicy = role === undefined ? config.orchestrator?.primary : agentExecutionPolicyForRole(role);
|
|
@@ -17604,8 +18180,8 @@ function createChatMessageHook(config, getAvailable, options = {}) {
|
|
|
17604
18180
|
function createHooks(config, getAvailable, options = {}) {
|
|
17605
18181
|
const residency = options.modelResidency ?? createModelResidencyGuard(config, { fetch: globalThis.fetch });
|
|
17606
18182
|
return {
|
|
17607
|
-
config: residency.config,
|
|
17608
|
-
"chat.params": residency.beforeInference,
|
|
18183
|
+
...residency.config === undefined ? {} : { config: residency.config },
|
|
18184
|
+
...residency.beforeInference === undefined ? {} : { "chat.params": residency.beforeInference },
|
|
17609
18185
|
"chat.message": createChatMessageHook(config, getAvailable, options)
|
|
17610
18186
|
};
|
|
17611
18187
|
}
|
|
@@ -18565,7 +19141,7 @@ var memoryToolMessages = {
|
|
|
18565
19141
|
scribeOnly: (actualRoleID) => `openteam memory: refused — memory records may only be written by the "scribe" role, not "${actualRoleID}".`,
|
|
18566
19142
|
disabled: "openteam memory: semantic memory is disabled; nothing was recorded.",
|
|
18567
19143
|
frontierDomainRefusal: 'openteam memory: refused — memory extraction requires local model execution, but router.executionMode is "frontier". Set router.executionMode to "local" or "mixed" and configure a reachable local runtime.',
|
|
18568
|
-
localRuntimeUnavailable: "openteam memory: memory extraction requires a local runtime that is reachable and will not fall back to a frontier provider. Configure Ollama, LM Studio, Lemonade, or Foundry Local and run `openteam doctor`.",
|
|
19144
|
+
localRuntimeUnavailable: "openteam memory: memory extraction requires a local runtime that is reachable and will not fall back to a frontier provider. Configure Ollama, LM Studio, Lemonade, llama-swap, or Foundry Local and run `openteam doctor`.",
|
|
18569
19145
|
localRuntimeBaseURLMissing: "openteam memory: refused — no local runtime with a base URL is configured and enabled; memory extraction is local-only.",
|
|
18570
19146
|
extractionFailure: (reason) => `openteam memory: extraction failed; nothing was recorded (${reason}).`,
|
|
18571
19147
|
noRecords: "openteam memory: no records were extracted; nothing was recorded.",
|
|
@@ -18635,14 +19211,16 @@ function createMemoryTool(deps) {
|
|
|
18635
19211
|
if (runtime?.baseURL === undefined || runtime.baseURL.length === 0) {
|
|
18636
19212
|
return memoryToolMessages.localRuntimeBaseURLMissing;
|
|
18637
19213
|
}
|
|
18638
|
-
const
|
|
19214
|
+
const kind = localRuntimeKind(runtime);
|
|
19215
|
+
const baseURL = kind === "llama-swap" ? llamaSwapBaseURL(runtime.baseURL) : runtime.baseURL;
|
|
18639
19216
|
const model = configuredModel ?? runtime.defaultModel;
|
|
18640
19217
|
const selectedRuntime = runtime;
|
|
18641
19218
|
const deadline = {
|
|
18642
19219
|
...deps.residencyOptions,
|
|
19220
|
+
...kind === "llama-swap" ? { runtimeLabel: "llama-swap" } : {},
|
|
18643
19221
|
...signal === undefined ? {} : { signal }
|
|
18644
19222
|
};
|
|
18645
|
-
const reachable =
|
|
19223
|
+
const reachable = runtimeRequiresModelResidency(kind) ? await withResidencyDeadline((signal2) => deps.localRuntimeReachable(selectedRuntime, signal2), deadline) : await deps.localRuntimeReachable(runtime);
|
|
18646
19224
|
throwIfAborted();
|
|
18647
19225
|
if (!reachable)
|
|
18648
19226
|
return memoryToolMessages.localRuntimeUnavailable;
|
|
@@ -18650,7 +19228,7 @@ function createMemoryTool(deps) {
|
|
|
18650
19228
|
const target = resolveTarget ? await withResidencyDeadline(async () => resolveTarget(model), deadline) : {
|
|
18651
19229
|
baseURL,
|
|
18652
19230
|
modelID: model.modelID,
|
|
18653
|
-
fetch:
|
|
19231
|
+
fetch: runtimeRequiresModelResidency(kind) ? (kind === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch)({
|
|
18654
19232
|
baseURL,
|
|
18655
19233
|
fetch: deps.fetch,
|
|
18656
19234
|
modelIDs: new Set([model.modelID]),
|
|
@@ -18753,12 +19331,841 @@ function createOrchestratedModelOwnership() {
|
|
|
18753
19331
|
};
|
|
18754
19332
|
}
|
|
18755
19333
|
|
|
19334
|
+
// src/plugin/nativeHost.ts
|
|
19335
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
19336
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
19337
|
+
import { resolve as resolve10 } from "node:path";
|
|
19338
|
+
import { Model as Model2 } from "@opencode/schema/model";
|
|
19339
|
+
import { Session as Session2 } from "@opencode/schema/session";
|
|
19340
|
+
import { Tool } from "@opencode/schema/tool";
|
|
19341
|
+
import { tool as legacyTool } from "@opencode-ai/plugin/tool";
|
|
19342
|
+
import { Clock, Effect, Option, Stream } from "effect";
|
|
19343
|
+
import { z as z30 } from "zod";
|
|
19344
|
+
|
|
19345
|
+
// src/plugin/nativeHostContext.ts
|
|
19346
|
+
import {
|
|
19347
|
+
Context
|
|
19348
|
+
} from "effect";
|
|
19349
|
+
|
|
19350
|
+
class NativeSessions extends Context.Service()("@opencode/Session") {
|
|
19351
|
+
}
|
|
19352
|
+
|
|
19353
|
+
class NativePermissions extends Context.Service()("@opencode/Permission") {
|
|
19354
|
+
}
|
|
19355
|
+
|
|
19356
|
+
class NativeAgents extends Context.Service()("@opencode/Agent") {
|
|
19357
|
+
}
|
|
19358
|
+
|
|
19359
|
+
class NativeLocations extends Context.Service()("@opencode/example/LocationServiceMap") {
|
|
19360
|
+
}
|
|
19361
|
+
|
|
19362
|
+
class NativeServerInfo extends Context.Service()("@opencode/server/ServerInfo") {
|
|
19363
|
+
}
|
|
19364
|
+
|
|
19365
|
+
class NativeServerAuth extends Context.Service()("@opencode/ServerAuthConfig") {
|
|
19366
|
+
}
|
|
19367
|
+
|
|
19368
|
+
// src/plugin/nativeInventory.ts
|
|
19369
|
+
function nativeConfiguredInventory(models, config) {
|
|
19370
|
+
const local = new Set(config.local.runtimes.map((runtime) => runtime.defaultModel.providerID));
|
|
19371
|
+
const enabled = models.filter((model) => model.enabled);
|
|
19372
|
+
const catalog = new Map;
|
|
19373
|
+
for (const model of enabled) {
|
|
19374
|
+
if (local.has(model.providerID))
|
|
19375
|
+
continue;
|
|
19376
|
+
const provider = catalog.get(model.providerID) ?? { models: [] };
|
|
19377
|
+
provider.models.push({
|
|
19378
|
+
id: model.id,
|
|
19379
|
+
name: model.name,
|
|
19380
|
+
limit: model.limit,
|
|
19381
|
+
cost: model.cost.find((tier) => tier.tier === undefined),
|
|
19382
|
+
status: model.status,
|
|
19383
|
+
tool_call: model.capabilities.tools,
|
|
19384
|
+
modalities: { input: model.capabilities.input },
|
|
19385
|
+
reasoning: model.capabilities.output.includes("reasoning")
|
|
19386
|
+
});
|
|
19387
|
+
catalog.set(model.providerID, provider);
|
|
19388
|
+
}
|
|
19389
|
+
return {
|
|
19390
|
+
models: enabled.map((model) => ({
|
|
19391
|
+
providerID: model.providerID,
|
|
19392
|
+
modelID: model.id,
|
|
19393
|
+
apiModelID: model.modelID,
|
|
19394
|
+
...local.has(model.providerID) ? {} : {
|
|
19395
|
+
supportsTools: model.capabilities.tools,
|
|
19396
|
+
...model.limit.context > 0 ? { contextWindow: model.limit.context } : {}
|
|
19397
|
+
}
|
|
19398
|
+
})),
|
|
19399
|
+
profiles: normalizeModelsDevCatalog(Object.fromEntries(catalog))
|
|
19400
|
+
};
|
|
19401
|
+
}
|
|
19402
|
+
|
|
19403
|
+
// src/plugin/nativeResidency.ts
|
|
19404
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
19405
|
+
import { z as z29 } from "zod";
|
|
19406
|
+
var NATIVE_RESIDENCY_PROVIDER_PACKAGE = "aisdk:@ai-sdk/openai-compatible@2.0.75";
|
|
19407
|
+
var NATIVE_ADMISSION_HEADER = "x-openteam-local-admission";
|
|
19408
|
+
var MAX_ADMISSIONS = 2048;
|
|
19409
|
+
var compatiblePackages = new Set([
|
|
19410
|
+
"@ai-sdk/openai-compatible",
|
|
19411
|
+
"aisdk:@ai-sdk/openai-compatible",
|
|
19412
|
+
"@opencode/ai/providers/openai-compatible",
|
|
19413
|
+
NATIVE_RESIDENCY_PROVIDER_PACKAGE
|
|
19414
|
+
]);
|
|
19415
|
+
function key(ref) {
|
|
19416
|
+
return `${ref.providerID}\x00${ref.modelID}`;
|
|
19417
|
+
}
|
|
19418
|
+
function label(runtime) {
|
|
19419
|
+
return localRuntimeKind(runtime) === "llama-swap" ? "llama-swap" : "Lemonade";
|
|
19420
|
+
}
|
|
19421
|
+
function runtimeBase2(runtime) {
|
|
19422
|
+
return localRuntimeKind(runtime) === "llama-swap" ? llamaSwapBaseURL(runtime.baseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL) : lemonadeBaseURL(runtime.baseURL ?? LEMONADE_DEFAULT_BASE_URL);
|
|
19423
|
+
}
|
|
19424
|
+
function createNativeModelResidencyGuard(config, options) {
|
|
19425
|
+
const admissions = new Map;
|
|
19426
|
+
const localTargets = new Map;
|
|
19427
|
+
let disposed = false;
|
|
19428
|
+
const residentRuntimes = config.local.runtimes.filter((runtime) => runtimeRequiresModelResidency(localRuntimeKind(runtime)));
|
|
19429
|
+
const globalMode = () => resolveExecutionMode({
|
|
19430
|
+
routerMode: config.router.executionMode,
|
|
19431
|
+
legacyLocalOnly: config.router.localOnly,
|
|
19432
|
+
legacyFrontierOnly: config.router.frontierOnly
|
|
19433
|
+
});
|
|
19434
|
+
function allowed(providerID, mode) {
|
|
19435
|
+
const local = config.local.runtimes.filter((runtime) => runtime.defaultModel.providerID === providerID);
|
|
19436
|
+
return local.length > 0 ? mode !== "frontier" && local.some((runtime) => runtime.enabled) : mode !== "local";
|
|
19437
|
+
}
|
|
19438
|
+
function protects(providerID, url) {
|
|
19439
|
+
return residentRuntimes.some((runtime) => {
|
|
19440
|
+
if (runtime.defaultModel.providerID === providerID)
|
|
19441
|
+
return true;
|
|
19442
|
+
if (url === undefined)
|
|
19443
|
+
return false;
|
|
19444
|
+
if (!URL.canParse(url))
|
|
19445
|
+
return true;
|
|
19446
|
+
return new URL(url).origin === new URL(runtimeBase2(runtime)).origin;
|
|
19447
|
+
});
|
|
19448
|
+
}
|
|
19449
|
+
function bind(model) {
|
|
19450
|
+
const runtimes = config.local.runtimes.filter((runtime2) => runtime2.defaultModel.providerID === model.providerID);
|
|
19451
|
+
const runtime = runtimes[0];
|
|
19452
|
+
const runtimeLabel = runtime === undefined ? undefined : label(runtime);
|
|
19453
|
+
if (runtimes.length !== 1 || runtime === undefined || !runtime.enabled)
|
|
19454
|
+
throw new ModelResidencyError("destination", runtimeLabel);
|
|
19455
|
+
const baseURL = runtimeBase2(runtime);
|
|
19456
|
+
const base = model.settings?.baseURL;
|
|
19457
|
+
const normalized = typeof base !== "string" ? undefined : localRuntimeKind(runtime) === "llama-swap" ? llamaSwapBaseURL(base) : lemonadeBaseURL(base);
|
|
19458
|
+
if (normalized !== baseURL)
|
|
19459
|
+
throw new ModelResidencyError("destination", runtimeLabel);
|
|
19460
|
+
if (model.package !== NATIVE_RESIDENCY_PROVIDER_PACKAGE || model.transport === "websocket" || model.settings?.fetch !== undefined || model.settings?.queryParams !== undefined || model.settings?.transformRequestBody !== undefined)
|
|
19461
|
+
throw new ModelResidencyError("transport", runtimeLabel);
|
|
19462
|
+
if (!model.modelID || model.modelID !== model.modelID.trim() || model.body?.model !== undefined && model.body.model !== model.modelID)
|
|
19463
|
+
throw new ModelResidencyError("identity", runtimeLabel);
|
|
19464
|
+
return { runtime, model, baseURL };
|
|
19465
|
+
}
|
|
19466
|
+
function guardedFetch(binding, send, authorized = () => true) {
|
|
19467
|
+
const create = localRuntimeKind(binding.runtime) === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch;
|
|
19468
|
+
return create({
|
|
19469
|
+
...options,
|
|
19470
|
+
fetch: Object.assign((input, init) => {
|
|
19471
|
+
if (disposed || !authorized())
|
|
19472
|
+
throw new Error(nativeHostMessages.missingAdmission);
|
|
19473
|
+
return send(input, init);
|
|
19474
|
+
}, {
|
|
19475
|
+
preconnect: (...args) => send.preconnect?.(...args)
|
|
19476
|
+
}),
|
|
19477
|
+
healthFetch: options.fetch,
|
|
19478
|
+
baseURL: binding.baseURL,
|
|
19479
|
+
modelIDs: new Set([binding.model.modelID])
|
|
19480
|
+
});
|
|
19481
|
+
}
|
|
19482
|
+
function target(model, send, baseURL) {
|
|
19483
|
+
const headers = new Headers(z29.record(z29.string(), z29.string()).parse(model.headers ?? {}));
|
|
19484
|
+
if (typeof model.settings?.apiKey === "string" && !headers.has("authorization"))
|
|
19485
|
+
headers.set("authorization", `Bearer ${model.settings.apiKey}`);
|
|
19486
|
+
return {
|
|
19487
|
+
baseURL,
|
|
19488
|
+
modelID: model.modelID,
|
|
19489
|
+
fetch: Object.assign(async (input, init) => {
|
|
19490
|
+
if (disposed)
|
|
19491
|
+
throw new Error(nativeHostMessages.missingAdmission);
|
|
19492
|
+
const request = new Request(input, init);
|
|
19493
|
+
for (const [name, value] of headers)
|
|
19494
|
+
if (!request.headers.has(name))
|
|
19495
|
+
request.headers.set(name, value);
|
|
19496
|
+
return send(request);
|
|
19497
|
+
}, {
|
|
19498
|
+
preconnect: (...args) => send.preconnect?.(...args)
|
|
19499
|
+
})
|
|
19500
|
+
};
|
|
19501
|
+
}
|
|
19502
|
+
function observeModels(models) {
|
|
19503
|
+
localTargets.clear();
|
|
19504
|
+
return models.map((model) => {
|
|
19505
|
+
if (!allowed(model.providerID, globalMode()))
|
|
19506
|
+
return { ...model, enabled: false };
|
|
19507
|
+
if (!model.enabled)
|
|
19508
|
+
return model;
|
|
19509
|
+
const ref = { providerID: model.providerID, modelID: model.id };
|
|
19510
|
+
const base = model.settings?.baseURL;
|
|
19511
|
+
if (!protects(model.providerID, typeof base === "string" ? base : undefined)) {
|
|
19512
|
+
if (config.local.runtimes.some((runtime) => runtime.enabled && runtime.defaultModel.providerID === model.providerID) && typeof base === "string")
|
|
19513
|
+
localTargets.set(key(ref), target(model, options.fetch, base));
|
|
19514
|
+
return model;
|
|
19515
|
+
}
|
|
19516
|
+
try {
|
|
19517
|
+
const binding = bind(model);
|
|
19518
|
+
localTargets.set(key(ref), target(model, guardedFetch(binding, options.fetch), binding.baseURL));
|
|
19519
|
+
return model;
|
|
19520
|
+
} catch (error) {
|
|
19521
|
+
if (!(error instanceof ModelResidencyError) && !(error instanceof z29.ZodError))
|
|
19522
|
+
throw error;
|
|
19523
|
+
(options.warn ?? console.warn)(modelResidencyMessages.providerDisabled(model.providerID, residentRuntimes.find((runtime) => runtime.defaultModel.providerID === model.providerID)?.kind === "llama-swap" ? "llama-swap" : "Lemonade"));
|
|
19524
|
+
return { ...model, enabled: false };
|
|
19525
|
+
}
|
|
19526
|
+
});
|
|
19527
|
+
}
|
|
19528
|
+
return {
|
|
19529
|
+
providers(editor) {
|
|
19530
|
+
for (const record of editor.list())
|
|
19531
|
+
if (!allowed(record.provider.id, globalMode()))
|
|
19532
|
+
editor.remove(record.provider.id);
|
|
19533
|
+
},
|
|
19534
|
+
transform(editor) {
|
|
19535
|
+
for (const model of editor.list()) {
|
|
19536
|
+
const provider = editor.provider.get(model.providerID)?.provider;
|
|
19537
|
+
const base = model.settings?.baseURL ?? provider?.settings?.baseURL;
|
|
19538
|
+
const pkg = model.package ?? provider?.package;
|
|
19539
|
+
if (protects(model.providerID, typeof base === "string" ? base : undefined) && pkg !== undefined && compatiblePackages.has(pkg))
|
|
19540
|
+
editor.update(model.providerID, model.id, (draft) => {
|
|
19541
|
+
draft.package = NATIVE_RESIDENCY_PROVIDER_PACKAGE;
|
|
19542
|
+
});
|
|
19543
|
+
}
|
|
19544
|
+
},
|
|
19545
|
+
observeModels,
|
|
19546
|
+
sdk(event) {
|
|
19547
|
+
if (!protects(event.model.providerID, typeof event.options.baseURL === "string" ? event.options.baseURL : undefined))
|
|
19548
|
+
return;
|
|
19549
|
+
const factory = bind(event.model);
|
|
19550
|
+
if (event.package !== NATIVE_RESIDENCY_PROVIDER_PACKAGE.slice("aisdk:".length) || typeof event.options.fetch !== "function")
|
|
19551
|
+
throw new Error(nativeHostMessages.transportUnavailable);
|
|
19552
|
+
const upstream = event.options.fetch;
|
|
19553
|
+
const transport = Object.assign((input, init) => {
|
|
19554
|
+
const request = new Request(input, init);
|
|
19555
|
+
return upstream(request, {
|
|
19556
|
+
signal: request.signal,
|
|
19557
|
+
redirect: "error"
|
|
19558
|
+
});
|
|
19559
|
+
}, {
|
|
19560
|
+
preconnect: (...args) => upstream.preconnect?.(...args)
|
|
19561
|
+
});
|
|
19562
|
+
event.options.fetch = Object.assign(async (input, init) => {
|
|
19563
|
+
let request = new Request(input, init);
|
|
19564
|
+
const token = request.headers.get(NATIVE_ADMISSION_HEADER);
|
|
19565
|
+
const admission = token === null ? undefined : admissions.get(token);
|
|
19566
|
+
if (token === null || admission === undefined || admission.model.providerID !== factory.model.providerID || admission.baseURL !== factory.baseURL)
|
|
19567
|
+
throw new Error(nativeHostMessages.missingAdmission);
|
|
19568
|
+
request.headers.delete(NATIVE_ADMISSION_HEADER);
|
|
19569
|
+
if (factory.model.body !== undefined) {
|
|
19570
|
+
let body;
|
|
19571
|
+
try {
|
|
19572
|
+
body = await request.json();
|
|
19573
|
+
} catch {
|
|
19574
|
+
throw new ModelResidencyError("request", label(factory.runtime));
|
|
19575
|
+
}
|
|
19576
|
+
if (body === null || typeof body !== "object" || Array.isArray(body))
|
|
19577
|
+
throw new ModelResidencyError("request", label(factory.runtime));
|
|
19578
|
+
request = new Request(request, {
|
|
19579
|
+
body: JSON.stringify(mergeConfigInputs(body, factory.model.body))
|
|
19580
|
+
});
|
|
19581
|
+
}
|
|
19582
|
+
return guardedFetch(admission, transport, () => admissions.get(token) === admission)(request);
|
|
19583
|
+
}, {
|
|
19584
|
+
preconnect: (...args) => upstream.preconnect?.(...args)
|
|
19585
|
+
});
|
|
19586
|
+
},
|
|
19587
|
+
async modelRequest(event) {
|
|
19588
|
+
if (disposed)
|
|
19589
|
+
throw new Error(nativeHostMessages.missingAdmission);
|
|
19590
|
+
const roleID = await options.resolveAgentRole?.(event.agent) ?? event.agent;
|
|
19591
|
+
const role = isKnownNonWorkerRole(roleID) ? undefined : getRoleProfile(roleID, config.orchestrator?.roles);
|
|
19592
|
+
const policy = role === undefined ? config.orchestrator?.primary : agentExecutionPolicyForRole(role);
|
|
19593
|
+
const mode = resolveExecutionMode({
|
|
19594
|
+
routerMode: globalMode(),
|
|
19595
|
+
agentMode: policy?.executionMode
|
|
19596
|
+
});
|
|
19597
|
+
if (!allowed(event.model.providerID, mode))
|
|
19598
|
+
throw new Error(nativeHostMessages.executionDomainBlocked);
|
|
19599
|
+
if (!protects(event.model.providerID, event.baseURL))
|
|
19600
|
+
return;
|
|
19601
|
+
const model = (await options.readModels()).find((candidate) => candidate.providerID === event.model.providerID && candidate.id === event.model.id && candidate.enabled);
|
|
19602
|
+
if (model === undefined)
|
|
19603
|
+
throw new Error(nativeHostMessages.transportUnavailable);
|
|
19604
|
+
const variant = model.variants.find((candidate) => candidate.id === event.model.variant);
|
|
19605
|
+
const effective = variant === undefined ? model : {
|
|
19606
|
+
...model,
|
|
19607
|
+
settings: { ...model.settings, ...variant.settings },
|
|
19608
|
+
headers: { ...model.headers, ...variant.headers },
|
|
19609
|
+
body: { ...model.body, ...variant.body }
|
|
19610
|
+
};
|
|
19611
|
+
const binding = bind(effective);
|
|
19612
|
+
if (disposed)
|
|
19613
|
+
throw new Error(nativeHostMessages.missingAdmission);
|
|
19614
|
+
const token = (options.newRequestID ?? randomUUID2)();
|
|
19615
|
+
admissions.set(token, {
|
|
19616
|
+
...binding,
|
|
19617
|
+
sessionID: event.sessionID,
|
|
19618
|
+
kind: event.kind
|
|
19619
|
+
});
|
|
19620
|
+
const oldest = admissions.keys().next().value;
|
|
19621
|
+
if (admissions.size > MAX_ADMISSIONS && oldest !== undefined)
|
|
19622
|
+
admissions.delete(oldest);
|
|
19623
|
+
event.headers[NATIVE_ADMISSION_HEADER] = token;
|
|
19624
|
+
},
|
|
19625
|
+
async http(event) {
|
|
19626
|
+
if (protects(event.model.providerID, event.request.url))
|
|
19627
|
+
throw new Error(nativeHostMessages.transportUnavailable);
|
|
19628
|
+
},
|
|
19629
|
+
websocket(event) {
|
|
19630
|
+
const url = new URL(event.url);
|
|
19631
|
+
if (url.protocol === "ws:")
|
|
19632
|
+
url.protocol = "http:";
|
|
19633
|
+
if (url.protocol === "wss:")
|
|
19634
|
+
url.protocol = "https:";
|
|
19635
|
+
if (protects(event.model.providerID, url.href))
|
|
19636
|
+
throw new Error(nativeHostMessages.transportUnavailable);
|
|
19637
|
+
},
|
|
19638
|
+
finished(sessionID) {
|
|
19639
|
+
for (const [token, admission] of admissions)
|
|
19640
|
+
if (admission.sessionID === sessionID)
|
|
19641
|
+
admissions.delete(token);
|
|
19642
|
+
},
|
|
19643
|
+
dispose() {
|
|
19644
|
+
disposed = true;
|
|
19645
|
+
admissions.clear();
|
|
19646
|
+
localTargets.clear();
|
|
19647
|
+
},
|
|
19648
|
+
localTarget(ref) {
|
|
19649
|
+
const value = localTargets.get(key(ref));
|
|
19650
|
+
if (value === undefined)
|
|
19651
|
+
throw new Error(nativeHostMessages.transportUnavailable);
|
|
19652
|
+
return value;
|
|
19653
|
+
}
|
|
19654
|
+
};
|
|
19655
|
+
}
|
|
19656
|
+
|
|
19657
|
+
// src/plugin/nativeSessions.ts
|
|
19658
|
+
import { resolve as resolve9 } from "node:path";
|
|
19659
|
+
import { Agent } from "@opencode/schema/agent";
|
|
19660
|
+
import { Model } from "@opencode/schema/model";
|
|
19661
|
+
import { Session } from "@opencode/schema/session";
|
|
19662
|
+
import { DateTime } from "effect";
|
|
19663
|
+
function sameNativeWorkspace(actual, expected) {
|
|
19664
|
+
return resolve9(actual.directory) === resolve9(expected.directory) && actual.workspaceID === expected.workspaceID;
|
|
19665
|
+
}
|
|
19666
|
+
function nativeAssistantResponse(sessionID, message) {
|
|
19667
|
+
return {
|
|
19668
|
+
info: {
|
|
19669
|
+
id: message.id,
|
|
19670
|
+
sessionID,
|
|
19671
|
+
role: "assistant",
|
|
19672
|
+
mode: message.agent,
|
|
19673
|
+
providerID: message.model.providerID,
|
|
19674
|
+
modelID: message.model.id,
|
|
19675
|
+
...message.cost === undefined ? {} : { cost: message.cost },
|
|
19676
|
+
...message.tokens === undefined ? {} : { tokens: message.tokens },
|
|
19677
|
+
...message.error === undefined ? {} : { error: message.error },
|
|
19678
|
+
time: {
|
|
19679
|
+
created: DateTime.toEpochMillis(message.time.created),
|
|
19680
|
+
...message.time.completed === undefined ? {} : { completed: DateTime.toEpochMillis(message.time.completed) }
|
|
19681
|
+
}
|
|
19682
|
+
},
|
|
19683
|
+
parts: message.content.flatMap((part) => part.type === "text" ? [{ type: "text", text: part.text }] : [])
|
|
19684
|
+
};
|
|
19685
|
+
}
|
|
19686
|
+
function createNativeSessionClient(deps) {
|
|
19687
|
+
const directory = resolve9(deps.directory);
|
|
19688
|
+
const location = {
|
|
19689
|
+
directory,
|
|
19690
|
+
...deps.workspaceID === undefined ? {} : { workspaceID: deps.workspaceID }
|
|
19691
|
+
};
|
|
19692
|
+
function assertDirectory(candidate) {
|
|
19693
|
+
if (candidate !== undefined && resolve9(candidate) !== directory)
|
|
19694
|
+
throw new Error(nativeHostMessages.workspaceMismatch);
|
|
19695
|
+
}
|
|
19696
|
+
async function ownedSession(id, signal) {
|
|
19697
|
+
const info = await deps.run(deps.sessions().get(id), signal === undefined ? undefined : { signal });
|
|
19698
|
+
if (!sameNativeWorkspace(info.location, location))
|
|
19699
|
+
throw new Error(nativeHostMessages.workspaceMismatch);
|
|
19700
|
+
return info;
|
|
19701
|
+
}
|
|
19702
|
+
return {
|
|
19703
|
+
async create(args) {
|
|
19704
|
+
const signal = deps.signal?.();
|
|
19705
|
+
signal?.throwIfAborted();
|
|
19706
|
+
assertDirectory(args.query?.directory);
|
|
19707
|
+
if (args.body?.parentID === undefined)
|
|
19708
|
+
throw new Error(nativeHostMessages.missingBindings);
|
|
19709
|
+
const parentID = Session.ID.make(args.body.parentID);
|
|
19710
|
+
await ownedSession(parentID, signal);
|
|
19711
|
+
const child = await deps.run(deps.sessions().create({
|
|
19712
|
+
parentID,
|
|
19713
|
+
...args.body.title === undefined ? {} : { title: args.body.title }
|
|
19714
|
+
}));
|
|
19715
|
+
if (!sameNativeWorkspace(child.location, location))
|
|
19716
|
+
throw new Error(nativeHostMessages.workspaceMismatch);
|
|
19717
|
+
if (child.parentID !== parentID)
|
|
19718
|
+
throw new Error(nativeHostMessages.missingBindings);
|
|
19719
|
+
signal?.throwIfAborted();
|
|
19720
|
+
return { data: { id: child.id }, error: undefined };
|
|
19721
|
+
},
|
|
19722
|
+
async prompt(args) {
|
|
19723
|
+
const parentSignal = deps.signal?.();
|
|
19724
|
+
const signal = parentSignal === undefined ? args.signal : args.signal === undefined ? parentSignal : AbortSignal.any([parentSignal, args.signal]);
|
|
19725
|
+
const sessionID = Session.ID.make(args.path.id);
|
|
19726
|
+
await ownedSession(sessionID, signal);
|
|
19727
|
+
const run = (effect) => deps.run(effect, signal === undefined ? undefined : { signal });
|
|
19728
|
+
const sessions = deps.sessions();
|
|
19729
|
+
try {
|
|
19730
|
+
if (args.body.agent !== undefined)
|
|
19731
|
+
await run(sessions.switchAgent({
|
|
19732
|
+
sessionID,
|
|
19733
|
+
agent: Agent.ID.make(args.body.agent)
|
|
19734
|
+
}));
|
|
19735
|
+
if (args.body.model !== undefined)
|
|
19736
|
+
await run(sessions.switchModel({
|
|
19737
|
+
sessionID,
|
|
19738
|
+
model: Model.Ref.parse(`${args.body.model.providerID}/${args.body.model.modelID}`)
|
|
19739
|
+
}));
|
|
19740
|
+
if (args.body.system !== undefined)
|
|
19741
|
+
deps.systemPrompts.set(sessionID, args.body.system);
|
|
19742
|
+
await run(sessions.prompt({
|
|
19743
|
+
sessionID,
|
|
19744
|
+
text: args.body.parts.map((part) => part.text).join(`
|
|
19745
|
+
`)
|
|
19746
|
+
}));
|
|
19747
|
+
await run(sessions.wait(sessionID));
|
|
19748
|
+
const final = await ownedSession(sessionID, signal);
|
|
19749
|
+
const messages = await run(sessions.messages({ sessionID, limit: 100 }));
|
|
19750
|
+
const assistant = [...messages].reverse().find((message) => message.type === "assistant");
|
|
19751
|
+
if (final.outcome === "interrupted")
|
|
19752
|
+
throw new Error(nativeHostMessages.executionInterrupted);
|
|
19753
|
+
if (final.outcome !== "succeeded" || assistant?.error !== undefined)
|
|
19754
|
+
throw new Error(nativeHostMessages.executionFailed);
|
|
19755
|
+
if (assistant?.time.completed === undefined)
|
|
19756
|
+
throw new Error(nativeHostMessages.noCompletedResponse);
|
|
19757
|
+
return {
|
|
19758
|
+
data: nativeAssistantResponse(sessionID, assistant),
|
|
19759
|
+
error: undefined
|
|
19760
|
+
};
|
|
19761
|
+
} finally {
|
|
19762
|
+
if (signal?.aborted)
|
|
19763
|
+
await deps.run(sessions.interrupt(sessionID, { resume: false }));
|
|
19764
|
+
}
|
|
19765
|
+
},
|
|
19766
|
+
async abort(args) {
|
|
19767
|
+
assertDirectory(args.query?.directory);
|
|
19768
|
+
const id = Session.ID.make(args.path.id);
|
|
19769
|
+
await ownedSession(id, args.signal);
|
|
19770
|
+
const options = args.signal === undefined ? undefined : { signal: args.signal };
|
|
19771
|
+
await deps.run(deps.sessions().interrupt(id, { resume: false }), options);
|
|
19772
|
+
const active = await deps.run(deps.sessions().active, options);
|
|
19773
|
+
return { data: !active.has(id), error: undefined };
|
|
19774
|
+
},
|
|
19775
|
+
async status(args) {
|
|
19776
|
+
assertDirectory(args?.query?.directory);
|
|
19777
|
+
const options = args?.signal == null ? undefined : { signal: args.signal };
|
|
19778
|
+
const sessions = deps.sessions();
|
|
19779
|
+
const active = await deps.run(sessions.active, options);
|
|
19780
|
+
const entries = [];
|
|
19781
|
+
for (const id of active) {
|
|
19782
|
+
const info = await deps.run(sessions.get(id), options);
|
|
19783
|
+
if (sameNativeWorkspace(info.location, location))
|
|
19784
|
+
entries.push([id, { type: "busy" }]);
|
|
19785
|
+
}
|
|
19786
|
+
return { data: Object.fromEntries(entries), error: undefined };
|
|
19787
|
+
}
|
|
19788
|
+
};
|
|
19789
|
+
}
|
|
19790
|
+
|
|
19791
|
+
// src/plugin/nativeHost.ts
|
|
19792
|
+
var executionEvent = z30.preprocess(only(["type", "location", "data"]), z30.object({
|
|
19793
|
+
type: z30.enum([
|
|
19794
|
+
"session.created",
|
|
19795
|
+
"session.deleted",
|
|
19796
|
+
"session.execution.started",
|
|
19797
|
+
"session.execution.succeeded",
|
|
19798
|
+
"session.execution.failed",
|
|
19799
|
+
"session.execution.interrupted",
|
|
19800
|
+
"session.step.ended",
|
|
19801
|
+
"session.step.failed"
|
|
19802
|
+
]),
|
|
19803
|
+
location: z30.preprocess(only(["directory", "workspaceID"]), z30.object({
|
|
19804
|
+
directory: z30.string(),
|
|
19805
|
+
workspaceID: z30.string().optional()
|
|
19806
|
+
}).strict()).optional(),
|
|
19807
|
+
data: z30.preprocess(only(["sessionID", "assistantMessageID"]), z30.object({
|
|
19808
|
+
sessionID: z30.string(),
|
|
19809
|
+
assistantMessageID: z30.string().optional()
|
|
19810
|
+
}).strict())
|
|
19811
|
+
}).strict());
|
|
19812
|
+
var nativeBindings = Effect.fn("openteam.nativeBindings")(function* () {
|
|
19813
|
+
const sessions = yield* Effect.serviceOption(NativeSessions);
|
|
19814
|
+
const permissions = yield* Effect.serviceOption(NativePermissions);
|
|
19815
|
+
const agents = yield* Effect.serviceOption(NativeAgents);
|
|
19816
|
+
const locations = yield* Effect.serviceOption(NativeLocations);
|
|
19817
|
+
if (Option.isNone(sessions) || Option.isNone(permissions) || Option.isNone(agents) || Option.isNone(locations))
|
|
19818
|
+
return yield* Effect.die(new Error(nativeHostMessages.missingBindings));
|
|
19819
|
+
const server = yield* Effect.serviceOption(NativeServerInfo);
|
|
19820
|
+
const auth = yield* Effect.serviceOption(NativeServerAuth);
|
|
19821
|
+
return {
|
|
19822
|
+
sessions: sessions.value,
|
|
19823
|
+
permissions: permissions.value,
|
|
19824
|
+
agents: agents.value,
|
|
19825
|
+
locations: locations.value,
|
|
19826
|
+
...Option.isNone(server) ? {} : { server: server.value },
|
|
19827
|
+
...Option.isNone(auth) ? {} : { auth: auth.value }
|
|
19828
|
+
};
|
|
19829
|
+
});
|
|
19830
|
+
function nativeExec(directory) {
|
|
19831
|
+
return (command, args) => new Promise((resolveOutput, reject) => {
|
|
19832
|
+
execFile3(command, [...args], { cwd: directory }, (error, stdout, stderr) => {
|
|
19833
|
+
if (error !== null && typeof error.code !== "number") {
|
|
19834
|
+
reject(error);
|
|
19835
|
+
return;
|
|
19836
|
+
}
|
|
19837
|
+
resolveOutput({
|
|
19838
|
+
exitCode: typeof error?.code === "number" ? error.code : 0,
|
|
19839
|
+
stdout,
|
|
19840
|
+
stderr
|
|
19841
|
+
});
|
|
19842
|
+
});
|
|
19843
|
+
});
|
|
19844
|
+
}
|
|
19845
|
+
function legacyModel(ref) {
|
|
19846
|
+
return { providerID: ref.providerID, modelID: ref.id };
|
|
19847
|
+
}
|
|
19848
|
+
function createNativePluginEffect(factory) {
|
|
19849
|
+
return Effect.fn("openteam.native")(function* (ctx) {
|
|
19850
|
+
if (ctx.app.version !== NATIVE_OPENCODE_VERSION)
|
|
19851
|
+
return yield* Effect.die(new Error(nativeHostMessages.unsupportedVersion(ctx.app.version)));
|
|
19852
|
+
const version = ctx.app.version;
|
|
19853
|
+
const effectContext = yield* Effect.context();
|
|
19854
|
+
const directory = resolve10(ctx.location.directory);
|
|
19855
|
+
let bindings;
|
|
19856
|
+
const run = (effect, options) => {
|
|
19857
|
+
const current = bindings;
|
|
19858
|
+
const bound = current === undefined ? effect : effect.pipe(Effect.provideService(NativeSessions, current.sessions), Effect.provideService(NativePermissions, current.permissions), Effect.provideService(NativeAgents, current.agents), Effect.provideService(NativeLocations, current.locations));
|
|
19859
|
+
return Effect.runPromiseWith(effectContext)(bound, options);
|
|
19860
|
+
};
|
|
19861
|
+
let config;
|
|
19862
|
+
let residency;
|
|
19863
|
+
const systemPrompts = new Map;
|
|
19864
|
+
const toolSignals = new AsyncLocalStorage;
|
|
19865
|
+
const remember = (next) => {
|
|
19866
|
+
bindings = { ...bindings, ...next };
|
|
19867
|
+
};
|
|
19868
|
+
const requiredBindings = () => {
|
|
19869
|
+
if (bindings === undefined)
|
|
19870
|
+
throw new Error(nativeHostMessages.missingBindings);
|
|
19871
|
+
return bindings;
|
|
19872
|
+
};
|
|
19873
|
+
const assertWorkspace = (candidate) => {
|
|
19874
|
+
if (!sameNativeWorkspace(candidate, ctx.location))
|
|
19875
|
+
throw new Error(nativeHostMessages.workspaceMismatch);
|
|
19876
|
+
};
|
|
19877
|
+
const session = createNativeSessionClient({
|
|
19878
|
+
directory,
|
|
19879
|
+
...ctx.location.workspaceID === undefined ? {} : { workspaceID: ctx.location.workspaceID },
|
|
19880
|
+
sessions: () => requiredBindings().sessions,
|
|
19881
|
+
run,
|
|
19882
|
+
systemPrompts,
|
|
19883
|
+
signal: () => toolSignals.getStore()
|
|
19884
|
+
});
|
|
19885
|
+
const client = {
|
|
19886
|
+
session,
|
|
19887
|
+
app: {
|
|
19888
|
+
async agents() {
|
|
19889
|
+
const result = await run(ctx.agent.list());
|
|
19890
|
+
return {
|
|
19891
|
+
data: result.data.map((agent) => ({ name: agent.id })),
|
|
19892
|
+
error: undefined
|
|
19893
|
+
};
|
|
19894
|
+
},
|
|
19895
|
+
async log(input) {
|
|
19896
|
+
const message = input.body?.message ?? nativeHostMessages.toolFailed;
|
|
19897
|
+
await run(input.body?.level === "error" ? Effect.logError(message) : input.body?.level === "warn" ? Effect.logWarning(message) : Effect.logInfo(message));
|
|
19898
|
+
}
|
|
19899
|
+
},
|
|
19900
|
+
instance: {
|
|
19901
|
+
async dispose(input) {
|
|
19902
|
+
assertWorkspace({
|
|
19903
|
+
...ctx.location,
|
|
19904
|
+
directory: input.query?.directory ?? directory
|
|
19905
|
+
});
|
|
19906
|
+
await run(requiredBindings().locations.invalidate({
|
|
19907
|
+
directory: ctx.location.directory,
|
|
19908
|
+
...ctx.location.workspaceID === undefined ? {} : { workspaceID: ctx.location.workspaceID }
|
|
19909
|
+
}), input.signal == null ? undefined : { signal: input.signal });
|
|
19910
|
+
return { data: true, error: undefined };
|
|
19911
|
+
}
|
|
19912
|
+
}
|
|
19913
|
+
};
|
|
19914
|
+
const hooks = yield* Effect.promise(() => factory({
|
|
19915
|
+
directory,
|
|
19916
|
+
worktree: ctx.location.project.directory,
|
|
19917
|
+
client,
|
|
19918
|
+
exec: nativeExec(directory),
|
|
19919
|
+
serverUrl: () => {
|
|
19920
|
+
const url = bindings?.server?.urls().find((value) => {
|
|
19921
|
+
if (!URL.canParse(value))
|
|
19922
|
+
return false;
|
|
19923
|
+
return ["127.0.0.1", "localhost", "[::1]"].includes(new URL(value).hostname);
|
|
19924
|
+
});
|
|
19925
|
+
return url === undefined ? undefined : new URL(url);
|
|
19926
|
+
},
|
|
19927
|
+
opencodeVersion: async () => version,
|
|
19928
|
+
listConfiguredInventory: async () => {
|
|
19929
|
+
if (config === undefined || residency === undefined)
|
|
19930
|
+
throw new Error(nativeHostMessages.missingBindings);
|
|
19931
|
+
const models = residency.observeModels((await run(ctx.model.list())).data);
|
|
19932
|
+
return nativeConfiguredInventory(models, config);
|
|
19933
|
+
},
|
|
19934
|
+
consoleEnvironment: () => {
|
|
19935
|
+
const auth = bindings?.auth;
|
|
19936
|
+
return {
|
|
19937
|
+
...process.env,
|
|
19938
|
+
BUN_BE_BUN: "1",
|
|
19939
|
+
OPENCODE_SERVER_USERNAME: auth?.username,
|
|
19940
|
+
OPENCODE_SERVER_PASSWORD: auth && Option.isSome(auth.password) ? auth.password.value : undefined
|
|
19941
|
+
};
|
|
19942
|
+
},
|
|
19943
|
+
createResidencyGuard: (runtimeConfig, options) => {
|
|
19944
|
+
config = runtimeConfig;
|
|
19945
|
+
residency = createNativeModelResidencyGuard(runtimeConfig, {
|
|
19946
|
+
...options,
|
|
19947
|
+
readModels: async () => (await run(ctx.model.list())).data
|
|
19948
|
+
});
|
|
19949
|
+
return residency;
|
|
19950
|
+
}
|
|
19951
|
+
}, ctx.options));
|
|
19952
|
+
const guard = residency;
|
|
19953
|
+
if (guard === undefined)
|
|
19954
|
+
return yield* Effect.die(new Error(nativeHostMessages.missingBindings));
|
|
19955
|
+
const chatMessage = hooks["chat.message"];
|
|
19956
|
+
const tools = hooks.tool;
|
|
19957
|
+
if (chatMessage === undefined || tools === undefined)
|
|
19958
|
+
return yield* Effect.die(new Error(nativeHostMessages.missingBindings));
|
|
19959
|
+
yield* Effect.addFinalizer(() => Effect.promise(async () => {
|
|
19960
|
+
guard.dispose();
|
|
19961
|
+
systemPrompts.clear();
|
|
19962
|
+
await hooks.dispose?.();
|
|
19963
|
+
}));
|
|
19964
|
+
yield* ctx.provider.transform(guard.providers);
|
|
19965
|
+
yield* ctx.model.transform(guard.transform);
|
|
19966
|
+
yield* ctx.aisdk.hook("sdk", (event) => Effect.sync(() => guard.sdk(event)));
|
|
19967
|
+
yield* ctx.session.hook("model.request", (event) => Effect.promise(() => guard.modelRequest(event)));
|
|
19968
|
+
yield* ctx.session.hook("http.request", (event) => Effect.promise(() => guard.http(event)));
|
|
19969
|
+
yield* ctx.session.hook("experimental.ws.handshake", (event) => Effect.sync(() => guard.websocket(event)));
|
|
19970
|
+
yield* ctx.session.hook("context", (event) => Effect.sync(() => {
|
|
19971
|
+
const system = systemPrompts.get(event.sessionID);
|
|
19972
|
+
if (system !== undefined)
|
|
19973
|
+
event.system.push({ type: "text", text: system });
|
|
19974
|
+
}));
|
|
19975
|
+
yield* ctx.session.hook("prompt", Effect.fn("openteam.nativePrompt")(function* (event) {
|
|
19976
|
+
const currentBindings = yield* nativeBindings();
|
|
19977
|
+
const current = yield* ctx.session.get({ sessionID: event.sessionID }).pipe(Effect.orDie);
|
|
19978
|
+
assertWorkspace(current.location);
|
|
19979
|
+
remember(currentBindings);
|
|
19980
|
+
const agent = yield* currentBindings.agents.resolve(current.agent).pipe(Effect.orDie);
|
|
19981
|
+
if (agent === undefined)
|
|
19982
|
+
return yield* Effect.die(new Error(nativeHostMessages.unknownAgent));
|
|
19983
|
+
const selected = current.model ?? (yield* ctx.model.default().pipe(Effect.orDie)).data ?? (yield* ctx.model.list().pipe(Effect.orDie)).data[0];
|
|
19984
|
+
if (selected === undefined)
|
|
19985
|
+
return yield* Effect.die(new Error(nativeHostMessages.noAvailableModel));
|
|
19986
|
+
const created = yield* Clock.currentTimeMillis;
|
|
19987
|
+
const output = {
|
|
19988
|
+
message: {
|
|
19989
|
+
id: event.messageID,
|
|
19990
|
+
sessionID: event.sessionID,
|
|
19991
|
+
role: "user",
|
|
19992
|
+
time: { created },
|
|
19993
|
+
agent: agent.id,
|
|
19994
|
+
model: legacyModel(selected)
|
|
19995
|
+
},
|
|
19996
|
+
parts: [
|
|
19997
|
+
{
|
|
19998
|
+
id: event.messageID,
|
|
19999
|
+
sessionID: event.sessionID,
|
|
20000
|
+
messageID: event.messageID,
|
|
20001
|
+
type: "text",
|
|
20002
|
+
text: event.prompt.text
|
|
20003
|
+
}
|
|
20004
|
+
]
|
|
20005
|
+
};
|
|
20006
|
+
yield* Effect.promise(() => chatMessage({
|
|
20007
|
+
sessionID: event.sessionID,
|
|
20008
|
+
messageID: event.messageID,
|
|
20009
|
+
agent: agent.id,
|
|
20010
|
+
...current.model === undefined ? {} : { model: legacyModel(current.model) }
|
|
20011
|
+
}, output));
|
|
20012
|
+
const routed = output.message.model;
|
|
20013
|
+
yield* ctx.session.switchModel({
|
|
20014
|
+
sessionID: event.sessionID,
|
|
20015
|
+
model: {
|
|
20016
|
+
...Model2.Ref.parse(`${routed.providerID}/${routed.modelID}`),
|
|
20017
|
+
...current.model?.providerID === routed.providerID && current.model.id === routed.modelID && current.model.variant !== undefined ? { variant: current.model.variant } : {}
|
|
20018
|
+
}
|
|
20019
|
+
}).pipe(Effect.orDie);
|
|
20020
|
+
const part = output.parts[0];
|
|
20021
|
+
if (part?.type === "text")
|
|
20022
|
+
event.prompt.text = part.text;
|
|
20023
|
+
}));
|
|
20024
|
+
yield* ctx.tool.transform((editor) => {
|
|
20025
|
+
for (const [name, definition] of Object.entries(tools)) {
|
|
20026
|
+
const input = legacyTool.schema.object(definition.args);
|
|
20027
|
+
editor.add({
|
|
20028
|
+
name,
|
|
20029
|
+
description: definition.description,
|
|
20030
|
+
input: legacyTool.schema.toJSONSchema(input, { io: "input" }),
|
|
20031
|
+
options: { codemode: false },
|
|
20032
|
+
execute: Effect.fn("openteam.nativeTool")(function* (_input, toolContext) {
|
|
20033
|
+
const current = requiredBindings();
|
|
20034
|
+
const parent = yield* current.sessions.get(toolContext.sessionID).pipe(Effect.orDie);
|
|
20035
|
+
assertWorkspace(parent.location);
|
|
20036
|
+
remember(current);
|
|
20037
|
+
return yield* Effect.tryPromise(async (signal) => {
|
|
20038
|
+
const progress = [];
|
|
20039
|
+
const context = {
|
|
20040
|
+
sessionID: toolContext.sessionID,
|
|
20041
|
+
messageID: toolContext.messageID,
|
|
20042
|
+
agent: toolContext.agent,
|
|
20043
|
+
directory,
|
|
20044
|
+
worktree: ctx.location.project.directory,
|
|
20045
|
+
abort: signal,
|
|
20046
|
+
metadata: (update) => {
|
|
20047
|
+
progress.push(run(toolContext.progress({
|
|
20048
|
+
...update.metadata,
|
|
20049
|
+
...update.title === undefined ? {} : { title: update.title }
|
|
20050
|
+
}), { signal }).then(() => ({}), (error) => ({ error })));
|
|
20051
|
+
},
|
|
20052
|
+
ask: async (request) => {
|
|
20053
|
+
await run(current.permissions.assert({
|
|
20054
|
+
sessionID: toolContext.sessionID,
|
|
20055
|
+
agent: toolContext.agent,
|
|
20056
|
+
action: request.permission,
|
|
20057
|
+
resources: request.patterns,
|
|
20058
|
+
save: request.always,
|
|
20059
|
+
metadata: request.metadata,
|
|
20060
|
+
source: {
|
|
20061
|
+
type: "tool",
|
|
20062
|
+
messageID: toolContext.messageID,
|
|
20063
|
+
id: toolContext.id
|
|
20064
|
+
}
|
|
20065
|
+
}), { signal });
|
|
20066
|
+
}
|
|
20067
|
+
};
|
|
20068
|
+
const result = await toolSignals.run(signal, () => definition.execute(input.parse(_input), context));
|
|
20069
|
+
const failed = (await Promise.all(progress)).find((entry) => entry.error !== undefined);
|
|
20070
|
+
if (failed)
|
|
20071
|
+
throw failed.error;
|
|
20072
|
+
if (typeof result === "string")
|
|
20073
|
+
return { content: result };
|
|
20074
|
+
return {
|
|
20075
|
+
content: [
|
|
20076
|
+
{ type: "text", text: result.output },
|
|
20077
|
+
...(result.attachments ?? []).map((attachment) => ({
|
|
20078
|
+
type: "file",
|
|
20079
|
+
uri: attachment.url,
|
|
20080
|
+
mime: attachment.mime,
|
|
20081
|
+
...attachment.filename === undefined ? {} : { name: attachment.filename }
|
|
20082
|
+
}))
|
|
20083
|
+
],
|
|
20084
|
+
metadata: {
|
|
20085
|
+
...result.metadata,
|
|
20086
|
+
...result.title === undefined ? {} : { title: result.title }
|
|
20087
|
+
}
|
|
20088
|
+
};
|
|
20089
|
+
}).pipe(Effect.mapError(() => new Tool.Error({ message: nativeHostMessages.toolFailed })));
|
|
20090
|
+
})
|
|
20091
|
+
});
|
|
20092
|
+
}
|
|
20093
|
+
});
|
|
20094
|
+
yield* ctx.tool.hook("execute.before", (event) => Effect.promise(() => hooks["tool.execute.before"]?.({ tool: event.tool, sessionID: event.sessionID, callID: event.id }, { args: event.input }) ?? Promise.resolve()));
|
|
20095
|
+
yield* ctx.tool.hook("execute.after", (event) => Effect.promise(() => hooks["tool.execute.after"]?.({
|
|
20096
|
+
tool: event.tool,
|
|
20097
|
+
sessionID: event.sessionID,
|
|
20098
|
+
callID: event.id,
|
|
20099
|
+
args: event.input
|
|
20100
|
+
}, { title: event.tool, output: "", metadata: {} }) ?? Promise.resolve()));
|
|
20101
|
+
yield* ctx.event.subscribe().pipe(Stream.runForEach((event) => Effect.tryPromise(async () => {
|
|
20102
|
+
const parsed = executionEvent.safeParse(event);
|
|
20103
|
+
if (!parsed.success)
|
|
20104
|
+
return;
|
|
20105
|
+
const value = parsed.data;
|
|
20106
|
+
if (value.location !== undefined) {
|
|
20107
|
+
if (!sameNativeWorkspace(value.location, ctx.location))
|
|
20108
|
+
return;
|
|
20109
|
+
} else if (value.type !== "session.deleted") {
|
|
20110
|
+
const info = await run(ctx.session.get({
|
|
20111
|
+
sessionID: Session2.ID.make(value.data.sessionID)
|
|
20112
|
+
}));
|
|
20113
|
+
if (!sameNativeWorkspace(info.location, ctx.location))
|
|
20114
|
+
return;
|
|
20115
|
+
} else
|
|
20116
|
+
return;
|
|
20117
|
+
const sessionID = value.data.sessionID;
|
|
20118
|
+
if (value.type === "session.deleted") {
|
|
20119
|
+
guard.finished(sessionID);
|
|
20120
|
+
systemPrompts.delete(sessionID);
|
|
20121
|
+
}
|
|
20122
|
+
let mapped;
|
|
20123
|
+
if (value.type === "session.created" || value.type === "session.deleted") {
|
|
20124
|
+
mapped = {
|
|
20125
|
+
type: value.type,
|
|
20126
|
+
properties: { info: { id: sessionID } }
|
|
20127
|
+
};
|
|
20128
|
+
} else if (value.type === "session.execution.started") {
|
|
20129
|
+
mapped = {
|
|
20130
|
+
type: "session.status",
|
|
20131
|
+
properties: { sessionID, status: { type: "busy" } }
|
|
20132
|
+
};
|
|
20133
|
+
} else if (value.type === "session.step.ended" || value.type === "session.step.failed") {
|
|
20134
|
+
if (bindings === undefined)
|
|
20135
|
+
return;
|
|
20136
|
+
const messages = await run(bindings.sessions.messages({
|
|
20137
|
+
sessionID: Session2.ID.make(sessionID),
|
|
20138
|
+
limit: 100
|
|
20139
|
+
}));
|
|
20140
|
+
const message = messages.find((candidate) => candidate.id === value.data.assistantMessageID);
|
|
20141
|
+
if (message?.type !== "assistant")
|
|
20142
|
+
return;
|
|
20143
|
+
mapped = {
|
|
20144
|
+
type: "message.updated",
|
|
20145
|
+
properties: {
|
|
20146
|
+
info: nativeAssistantResponse(sessionID, message).info
|
|
20147
|
+
}
|
|
20148
|
+
};
|
|
20149
|
+
} else {
|
|
20150
|
+
systemPrompts.delete(sessionID);
|
|
20151
|
+
guard.finished(sessionID);
|
|
20152
|
+
if (value.type !== "session.execution.succeeded")
|
|
20153
|
+
await hooks.event?.({
|
|
20154
|
+
event: { type: "session.error", properties: { sessionID } }
|
|
20155
|
+
});
|
|
20156
|
+
mapped = { type: "session.idle", properties: { sessionID } };
|
|
20157
|
+
}
|
|
20158
|
+
await hooks.event?.({ event: mapped });
|
|
20159
|
+
}).pipe(Effect.catch(() => Effect.logError(nativeHostMessages.eventCaptureFailed)))), Effect.tapCause(() => Effect.logError(nativeHostMessages.eventCaptureFailed)), Effect.forkScoped);
|
|
20160
|
+
});
|
|
20161
|
+
}
|
|
20162
|
+
|
|
18756
20163
|
// src/plugin/orchestrateTool.ts
|
|
18757
20164
|
import { tool as tool5 } from "@opencode-ai/plugin";
|
|
18758
|
-
import { z as
|
|
20165
|
+
import { z as z32 } from "zod";
|
|
18759
20166
|
|
|
18760
20167
|
// src/orchestrator/coordinator.ts
|
|
18761
|
-
import { z as
|
|
20168
|
+
import { z as z31 } from "zod";
|
|
18762
20169
|
|
|
18763
20170
|
// src/orchestrator/nodeIteration.ts
|
|
18764
20171
|
var DEFAULT_MAX_RETRIES = 2;
|
|
@@ -19232,7 +20639,7 @@ function buildRecord(input, decision2, subsession, deps, decisionID, batchID) {
|
|
|
19232
20639
|
}
|
|
19233
20640
|
return record;
|
|
19234
20641
|
}
|
|
19235
|
-
var TelemetryFailureClassSchema =
|
|
20642
|
+
var TelemetryFailureClassSchema = z31.enum([
|
|
19236
20643
|
"create-rejected",
|
|
19237
20644
|
"create-no-id",
|
|
19238
20645
|
"prompt-rejected",
|
|
@@ -19658,10 +21065,10 @@ function uniqueModels(models) {
|
|
|
19658
21065
|
const seen = new Set;
|
|
19659
21066
|
const unique = [];
|
|
19660
21067
|
for (const model of models) {
|
|
19661
|
-
const
|
|
19662
|
-
if (seen.has(
|
|
21068
|
+
const key2 = modelKey4(model);
|
|
21069
|
+
if (seen.has(key2))
|
|
19663
21070
|
continue;
|
|
19664
|
-
seen.add(
|
|
21071
|
+
seen.add(key2);
|
|
19665
21072
|
unique.push(model);
|
|
19666
21073
|
}
|
|
19667
21074
|
return unique;
|
|
@@ -20049,7 +21456,7 @@ async function runRoleTasks(inputs, deps, options = {}) {
|
|
|
20049
21456
|
var _pluginWarnedRoles = new Set;
|
|
20050
21457
|
var EMPTY_ROLE_ID_SET = new Set;
|
|
20051
21458
|
var SAFE_ROLE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
20052
|
-
var RoleIDSchema =
|
|
21459
|
+
var RoleIDSchema = z32.string().min(1).superRefine((roleID, ctx) => {
|
|
20053
21460
|
if (!SAFE_ROLE_ID.test(roleID)) {
|
|
20054
21461
|
ctx.addIssue({
|
|
20055
21462
|
code: "custom",
|
|
@@ -20057,16 +21464,16 @@ var RoleIDSchema = z30.string().min(1).superRefine((roleID, ctx) => {
|
|
|
20057
21464
|
});
|
|
20058
21465
|
}
|
|
20059
21466
|
});
|
|
20060
|
-
var RoleAssignmentSchema =
|
|
21467
|
+
var RoleAssignmentSchema = z32.object({
|
|
20061
21468
|
roleID: RoleIDSchema.describe("Role identifier from the roster"),
|
|
20062
|
-
prompt:
|
|
20063
|
-
title:
|
|
20064
|
-
dependsOn:
|
|
21469
|
+
prompt: z32.string().min(1).describe("Work prompt for this role"),
|
|
21470
|
+
title: z32.string().min(1).optional().describe("Human-readable title"),
|
|
21471
|
+
dependsOn: z32.array(RoleIDSchema).optional().describe("Role IDs this assignment depends on (DAG edges)")
|
|
20065
21472
|
}).strict();
|
|
20066
|
-
var OrchestratePayloadSchema =
|
|
20067
|
-
assignments:
|
|
20068
|
-
parentSessionID:
|
|
20069
|
-
directory:
|
|
21473
|
+
var OrchestratePayloadSchema = z32.object({
|
|
21474
|
+
assignments: z32.array(RoleAssignmentSchema).min(1).describe("Role assignments to distribute"),
|
|
21475
|
+
parentSessionID: z32.string().min(1).optional().describe(PARENT_SESSION_DESCRIPTION),
|
|
21476
|
+
directory: z32.string().min(1).optional().describe("Working directory override")
|
|
20070
21477
|
}).strict();
|
|
20071
21478
|
function validateAssignmentDependencies(assignments) {
|
|
20072
21479
|
const ids = new Set;
|
|
@@ -20127,14 +21534,14 @@ function validateAssignmentDependencies(assignments) {
|
|
|
20127
21534
|
function isRecord7(value) {
|
|
20128
21535
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20129
21536
|
}
|
|
20130
|
-
function nestedRecord2(value,
|
|
20131
|
-
const nested = value[
|
|
21537
|
+
function nestedRecord2(value, key2) {
|
|
21538
|
+
const nested = value[key2];
|
|
20132
21539
|
return isRecord7(nested) ? nested : undefined;
|
|
20133
21540
|
}
|
|
20134
21541
|
function numberField(records, keys) {
|
|
20135
21542
|
for (const record of records) {
|
|
20136
|
-
for (const
|
|
20137
|
-
const value = record[
|
|
21543
|
+
for (const key2 of keys) {
|
|
21544
|
+
const value = record[key2];
|
|
20138
21545
|
if (typeof value === "number" && Number.isInteger(value)) {
|
|
20139
21546
|
return value;
|
|
20140
21547
|
}
|
|
@@ -20144,8 +21551,8 @@ function numberField(records, keys) {
|
|
|
20144
21551
|
}
|
|
20145
21552
|
function booleanField(records, keys) {
|
|
20146
21553
|
for (const record of records) {
|
|
20147
|
-
for (const
|
|
20148
|
-
const value = record[
|
|
21554
|
+
for (const key2 of keys) {
|
|
21555
|
+
const value = record[key2];
|
|
20149
21556
|
if (typeof value === "boolean") {
|
|
20150
21557
|
return value;
|
|
20151
21558
|
}
|
|
@@ -20156,8 +21563,8 @@ function booleanField(records, keys) {
|
|
|
20156
21563
|
function boundedErrorTokens(records) {
|
|
20157
21564
|
const tokens = [];
|
|
20158
21565
|
for (const record of records) {
|
|
20159
|
-
for (const
|
|
20160
|
-
const value = record[
|
|
21566
|
+
for (const key2 of ["name", "code", "type", "message"]) {
|
|
21567
|
+
const value = record[key2];
|
|
20161
21568
|
if (typeof value === "string" && value.length <= 100) {
|
|
20162
21569
|
tokens.push(value.toLowerCase());
|
|
20163
21570
|
}
|
|
@@ -20695,6 +22102,8 @@ function createOrchestrateTool(deps) {
|
|
|
20695
22102
|
}
|
|
20696
22103
|
const parentOverrideIgnored = parsed.data.parentSessionID !== undefined && parsed.data.parentSessionID !== context.sessionID;
|
|
20697
22104
|
parsed.data.parentSessionID = context.sessionID;
|
|
22105
|
+
if (parsed.data.directory === undefined && context.directory !== undefined)
|
|
22106
|
+
parsed.data.directory = context.directory;
|
|
20698
22107
|
const depError = validateAssignmentDependencies(parsed.data.assignments);
|
|
20699
22108
|
if (depError !== undefined) {
|
|
20700
22109
|
return dependencyValidationError(depError.code, depError.detail);
|
|
@@ -20802,7 +22211,7 @@ ${advisory}`;
|
|
|
20802
22211
|
|
|
20803
22212
|
// src/plugin/registerCastTool.ts
|
|
20804
22213
|
import { tool as tool6 } from "@opencode-ai/plugin";
|
|
20805
|
-
import { z as
|
|
22214
|
+
import { z as z33 } from "zod";
|
|
20806
22215
|
|
|
20807
22216
|
// src/messages/rosterSummary.ts
|
|
20808
22217
|
var ROSTER_SUMMARY_HEADING = "Registered team model policies";
|
|
@@ -20918,12 +22327,12 @@ function createTeamPreparation(deps) {
|
|
|
20918
22327
|
OpenTeamConfigSchema.parse(migrateConfig(baseRaw));
|
|
20919
22328
|
const overlay = overlayBefore === undefined ? undefined : parseConfigFileJson(DEFAULT_LOCAL_OVERLAY_PATH, overlayBefore);
|
|
20920
22329
|
const options = configOptionsFromPluginOptions(deps.options);
|
|
20921
|
-
const
|
|
22330
|
+
const resolve11 = (raw) => {
|
|
20922
22331
|
const config = validateConfigInput(TEAM_PREPARATION_CONFIG, mergeConfigInputs(mergeConfigInputs(raw, options), overlay));
|
|
20923
22332
|
assertEffectiveConfigModelDomains(config);
|
|
20924
22333
|
return config;
|
|
20925
22334
|
};
|
|
20926
|
-
const current =
|
|
22335
|
+
const current = resolve11(baseRaw);
|
|
20927
22336
|
const live = liveConfig ?? current;
|
|
20928
22337
|
if (resolveExecutionMode({
|
|
20929
22338
|
routerMode: current.router.executionMode,
|
|
@@ -20945,9 +22354,9 @@ function createTeamPreparation(deps) {
|
|
|
20945
22354
|
]))
|
|
20946
22355
|
}
|
|
20947
22356
|
});
|
|
20948
|
-
const effective =
|
|
22357
|
+
const effective = resolve11(candidate);
|
|
20949
22358
|
const requestedProfiles = new Set(roster.entries.filter((entry) => !isKnownNonWorkerRole(entry.roleID) && getRoleProfile(entry.roleID, live.orchestrator?.roles) === undefined).map((entry) => getRoleProfile(entry.roleID, effective.orchestrator?.roles)));
|
|
20950
|
-
const additions = Object.fromEntries(Object.entries(effective.orchestrator?.roles ?? {}).filter(([
|
|
22359
|
+
const additions = Object.fromEntries(Object.entries(effective.orchestrator?.roles ?? {}).filter(([key2, profile]) => requestedProfiles.has(profile) && !Object.hasOwn(live.orchestrator?.roles ?? {}, key2)));
|
|
20951
22360
|
const hasAdditions = Object.keys(additions).length > 0;
|
|
20952
22361
|
const next = hasAdditions ? validateConfigInput(TEAM_PREPARATION_CONFIG, {
|
|
20953
22362
|
...live,
|
|
@@ -21031,19 +22440,19 @@ async function persistPreparedTeam(storage, files) {
|
|
|
21031
22440
|
}
|
|
21032
22441
|
|
|
21033
22442
|
// src/plugin/registerCastTool.ts
|
|
21034
|
-
var CastEntrySchema =
|
|
21035
|
-
roleID:
|
|
21036
|
-
agentName:
|
|
22443
|
+
var CastEntrySchema = z33.object({
|
|
22444
|
+
roleID: z33.string().min(1).describe("Stable role identifier, e.g. scribe or guardian"),
|
|
22445
|
+
agentName: z33.string().min(1).describe("Themed character name assigned to the role")
|
|
21037
22446
|
}).strict();
|
|
21038
|
-
var RegisterCastPayloadSchema =
|
|
21039
|
-
universe:
|
|
21040
|
-
entries:
|
|
22447
|
+
var RegisterCastPayloadSchema = z33.object({
|
|
22448
|
+
universe: z33.string().min(1).describe("Casting universe for this roster"),
|
|
22449
|
+
entries: z33.array(CastEntrySchema).min(1).describe("Role-to-name mappings")
|
|
21041
22450
|
}).strict();
|
|
21042
22451
|
function offendingFields(error) {
|
|
21043
22452
|
const fields = error.issues.flatMap((issue) => {
|
|
21044
22453
|
const base = issue.path.map((segment) => String(segment));
|
|
21045
22454
|
if (issue.code === "unrecognized_keys") {
|
|
21046
|
-
return issue.keys.map((
|
|
22455
|
+
return issue.keys.map((key2) => [...base, key2].join("."));
|
|
21047
22456
|
}
|
|
21048
22457
|
return [base.join(".") || "(root)"];
|
|
21049
22458
|
});
|
|
@@ -21083,12 +22492,12 @@ function markdownCell(value) {
|
|
|
21083
22492
|
}
|
|
21084
22493
|
function modelPolicyLabel(policy) {
|
|
21085
22494
|
const model = policy?.model;
|
|
21086
|
-
const
|
|
22495
|
+
const label2 = model === undefined || model === "auto" ? ROSTER_AUTO_MODEL : `${model.providerID}/${model.modelID}`;
|
|
21087
22496
|
if (policy?.fallbacks === undefined || policy.fallbacks.length === 0) {
|
|
21088
|
-
return
|
|
22497
|
+
return label2;
|
|
21089
22498
|
}
|
|
21090
22499
|
const fallbacks = policy.fallbacks.map((fallback) => `${fallback.providerID}/${fallback.modelID}`);
|
|
21091
|
-
return `${
|
|
22500
|
+
return `${label2}; ${rosterOrderedFallbacks(fallbacks)}`;
|
|
21092
22501
|
}
|
|
21093
22502
|
function registeredTeamTable(roster, deps) {
|
|
21094
22503
|
const rows = roster.entries.map((entry) => {
|
|
@@ -21157,7 +22566,7 @@ function createRegisterCastTool(deps) {
|
|
|
21157
22566
|
}
|
|
21158
22567
|
|
|
21159
22568
|
// src/plugin/reloadTool.ts
|
|
21160
|
-
import { resolve as
|
|
22569
|
+
import { resolve as resolve11 } from "node:path";
|
|
21161
22570
|
import { tool as tool7 } from "@opencode-ai/plugin/tool";
|
|
21162
22571
|
var WAIT_MS = 30000;
|
|
21163
22572
|
var SDK_TIMEOUT_MS = 5000;
|
|
@@ -21191,7 +22600,7 @@ function createReloadController(deps) {
|
|
|
21191
22600
|
return () => clearTimeout(timer);
|
|
21192
22601
|
}
|
|
21193
22602
|
};
|
|
21194
|
-
const directory =
|
|
22603
|
+
const directory = resolve11(deps.directory);
|
|
21195
22604
|
let pending;
|
|
21196
22605
|
let authorizing;
|
|
21197
22606
|
let closed = false;
|
|
@@ -21339,7 +22748,7 @@ function createReloadController(deps) {
|
|
|
21339
22748
|
const parsed = schema.safeParse(args);
|
|
21340
22749
|
if (!parsed.success)
|
|
21341
22750
|
return reloadMessages.invalid;
|
|
21342
|
-
if (context.agent !== "openteam" ||
|
|
22751
|
+
if (context.agent !== "openteam" || resolve11(context.directory) !== directory || context.sessionID.trim().length === 0)
|
|
21343
22752
|
return reloadMessages.denied;
|
|
21344
22753
|
if (parsed.data.action === "cancel") {
|
|
21345
22754
|
if ((pending?.sessionID ?? authorizing?.sessionID) !== context.sessionID)
|
|
@@ -21485,7 +22894,7 @@ function createSessionTracker() {
|
|
|
21485
22894
|
}
|
|
21486
22895
|
|
|
21487
22896
|
// src/plugin/toolcalls.ts
|
|
21488
|
-
function
|
|
22897
|
+
function key2(sessionID, callID) {
|
|
21489
22898
|
return `${sessionID}\x00${callID}`;
|
|
21490
22899
|
}
|
|
21491
22900
|
function createToolcallTracker(deps) {
|
|
@@ -21493,10 +22902,10 @@ function createToolcallTracker(deps) {
|
|
|
21493
22902
|
const started = new Map;
|
|
21494
22903
|
return {
|
|
21495
22904
|
before(input) {
|
|
21496
|
-
started.set(
|
|
22905
|
+
started.set(key2(input.sessionID, input.callID), now());
|
|
21497
22906
|
},
|
|
21498
22907
|
after(input) {
|
|
21499
|
-
const mapKey =
|
|
22908
|
+
const mapKey = key2(input.sessionID, input.callID);
|
|
21500
22909
|
const start = started.get(mapKey);
|
|
21501
22910
|
started.delete(mapKey);
|
|
21502
22911
|
const end = now();
|
|
@@ -21569,6 +22978,13 @@ function createLocalEmbedder(request, deps) {
|
|
|
21569
22978
|
return (texts) => embedTexts({ ...request, texts }, deps);
|
|
21570
22979
|
}
|
|
21571
22980
|
|
|
22981
|
+
// src/messages/memoryRuntime.ts
|
|
22982
|
+
var memoryRuntimeMessages = {
|
|
22983
|
+
providerUnavailable: "[openteam] Semantic memory requires exactly one enabled local runtime matching the configured embeddings provider. No embedding request was sent.",
|
|
22984
|
+
unavailable: "[openteam] Semantic memory initialization failed; recall and memory injection are unavailable.",
|
|
22985
|
+
rejectedLines: (count) => `[openteam] memory log rejected ${count} line(s); semantic memory may be incomplete.`
|
|
22986
|
+
};
|
|
22987
|
+
|
|
21572
22988
|
// src/storage/index/bunSqlite.ts
|
|
21573
22989
|
async function createBunSqliteDatabase(path4) {
|
|
21574
22990
|
const mod = await import("bun:sqlite");
|
|
@@ -21652,7 +23068,14 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
|
|
|
21652
23068
|
if (options.requireInjectionEnabled === true && !semantic.injection.enabled) {
|
|
21653
23069
|
return;
|
|
21654
23070
|
}
|
|
21655
|
-
const
|
|
23071
|
+
const warn = deps.warn ?? console.warn;
|
|
23072
|
+
const configuredModel = semantic.embeddings.model;
|
|
23073
|
+
const candidates = configuredModel == null ? config.local.runtimes.filter((rt) => rt.enabled && rt.baseURL !== undefined) : config.local.runtimes.filter((rt) => rt.defaultModel.providerID === configuredModel.providerID);
|
|
23074
|
+
const runtime = candidates[0];
|
|
23075
|
+
if (configuredModel != null && (candidates.length !== 1 || runtime?.enabled !== true)) {
|
|
23076
|
+
warn(memoryRuntimeMessages.providerUnavailable);
|
|
23077
|
+
return;
|
|
23078
|
+
}
|
|
21656
23079
|
if (runtime?.baseURL === undefined) {
|
|
21657
23080
|
return;
|
|
21658
23081
|
}
|
|
@@ -21661,15 +23084,22 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
|
|
|
21661
23084
|
const makeEmbedder = deps.createEmbedder ?? createLocalEmbedder;
|
|
21662
23085
|
const fetchImpl = deps.fetch ?? globalThis.fetch;
|
|
21663
23086
|
const now = deps.now ?? (() => Date.now());
|
|
21664
|
-
const
|
|
23087
|
+
const kind = localRuntimeKind(runtime);
|
|
21665
23088
|
try {
|
|
23089
|
+
const baseURL = kind === "llama-swap" ? llamaSwapBaseURL(runtime.baseURL) : runtime.baseURL;
|
|
23090
|
+
const guardedFetch = runtimeRequiresModelResidency(kind) ? (kind === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch)({
|
|
23091
|
+
baseURL,
|
|
23092
|
+
fetch: fetchImpl,
|
|
23093
|
+
healthFetch: fetchImpl,
|
|
23094
|
+
modelIDs: new Set([modelID2])
|
|
23095
|
+
}) : fetchImpl;
|
|
21666
23096
|
const db = await openDatabase(semantic.indexPath);
|
|
21667
23097
|
const index = createMemoryIndex(db, warn);
|
|
21668
23098
|
const rebuildResult = await rebuildMemoryIndexFromStorage(index, semantic.logPath, { storage: deps.storage });
|
|
21669
23099
|
if (rebuildResult.rejectedLines > 0) {
|
|
21670
|
-
warn(
|
|
23100
|
+
warn(memoryRuntimeMessages.rejectedLines(rebuildResult.rejectedLines));
|
|
21671
23101
|
}
|
|
21672
|
-
const embedder = makeEmbedder({ baseURL
|
|
23102
|
+
const embedder = makeEmbedder({ baseURL, modelID: modelID2 }, { fetch: guardedFetch });
|
|
21673
23103
|
await ensureMemoryEmbeddings({ index, embedder, model: modelID2, warn });
|
|
21674
23104
|
return {
|
|
21675
23105
|
index,
|
|
@@ -21679,12 +23109,13 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
|
|
|
21679
23109
|
semantic
|
|
21680
23110
|
};
|
|
21681
23111
|
} catch {
|
|
23112
|
+
warn(memoryRuntimeMessages.unavailable);
|
|
21682
23113
|
return;
|
|
21683
23114
|
}
|
|
21684
23115
|
}
|
|
21685
23116
|
|
|
21686
23117
|
// src/storage/lifecycle/fsLifecycleJournal.ts
|
|
21687
|
-
import { randomUUID as
|
|
23118
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
21688
23119
|
import {
|
|
21689
23120
|
lstatSync as lstatSync3,
|
|
21690
23121
|
mkdirSync as mkdirSync3,
|
|
@@ -21694,22 +23125,22 @@ import {
|
|
|
21694
23125
|
statSync,
|
|
21695
23126
|
unlinkSync as unlinkSync2
|
|
21696
23127
|
} from "node:fs";
|
|
21697
|
-
import { join as join15, resolve as
|
|
23128
|
+
import { join as join15, resolve as resolve13 } from "node:path";
|
|
21698
23129
|
import { ZodError } from "zod";
|
|
21699
23130
|
|
|
21700
23131
|
// src/storage/lifecycle/codec.ts
|
|
21701
|
-
import { z as
|
|
23132
|
+
import { z as z34 } from "zod";
|
|
21702
23133
|
var LIFECYCLE_CODEC_VERSION = 1;
|
|
21703
23134
|
var LIFECYCLE_GENESIS_DIGEST = sha256Hex("openteam/lifecycle-journal/genesis/v1");
|
|
21704
23135
|
var NUL5 = "\x00";
|
|
21705
23136
|
var NEWLINE2 = `
|
|
21706
23137
|
`;
|
|
21707
|
-
var frameSchema =
|
|
21708
|
-
v:
|
|
21709
|
-
seq:
|
|
21710
|
-
prev:
|
|
21711
|
-
sum:
|
|
21712
|
-
event:
|
|
23138
|
+
var frameSchema = z34.object({
|
|
23139
|
+
v: z34.literal(LIFECYCLE_CODEC_VERSION),
|
|
23140
|
+
seq: z34.number().int().nonnegative(),
|
|
23141
|
+
prev: z34.string().min(1),
|
|
23142
|
+
sum: z34.string().min(1),
|
|
23143
|
+
event: z34.unknown()
|
|
21713
23144
|
}).strict();
|
|
21714
23145
|
function canonical2(value) {
|
|
21715
23146
|
if (Array.isArray(value)) {
|
|
@@ -21718,7 +23149,7 @@ function canonical2(value) {
|
|
|
21718
23149
|
if (value === null || typeof value !== "object") {
|
|
21719
23150
|
return JSON.stringify(value);
|
|
21720
23151
|
}
|
|
21721
|
-
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([
|
|
23152
|
+
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key3, child]) => `${JSON.stringify(key3)}:${canonical2(child)}`).join(",")}}`;
|
|
21722
23153
|
}
|
|
21723
23154
|
function lifecycleFrameDigest(previousDigest, event) {
|
|
21724
23155
|
return sha256Hex(`${previousDigest}${NUL5}${canonical2(event)}`);
|
|
@@ -21796,7 +23227,7 @@ function decodeLifecycleJournal(text) {
|
|
|
21796
23227
|
}
|
|
21797
23228
|
|
|
21798
23229
|
// src/storage/lifecycle/fsLifecycleSupport.ts
|
|
21799
|
-
import { randomUUID as
|
|
23230
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
21800
23231
|
import {
|
|
21801
23232
|
appendFileSync,
|
|
21802
23233
|
closeSync as closeSync2,
|
|
@@ -21813,7 +23244,7 @@ import {
|
|
|
21813
23244
|
unlinkSync,
|
|
21814
23245
|
writeSync as writeSync2
|
|
21815
23246
|
} from "node:fs";
|
|
21816
|
-
import { basename as basename3, dirname as dirname7, join as join14, relative as relative3, resolve as
|
|
23247
|
+
import { basename as basename3, dirname as dirname7, join as join14, relative as relative3, resolve as resolve12 } from "node:path";
|
|
21817
23248
|
var SAFE_RUN_ID = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,126}[A-Za-z0-9])?$/u;
|
|
21818
23249
|
|
|
21819
23250
|
class LifecycleFsError extends Error {
|
|
@@ -21837,7 +23268,7 @@ function validateRunID(runID) {
|
|
|
21837
23268
|
}
|
|
21838
23269
|
function resolvedLifecycleRoot(root) {
|
|
21839
23270
|
try {
|
|
21840
|
-
const absolute =
|
|
23271
|
+
const absolute = resolve12(root);
|
|
21841
23272
|
mkdirSync2(absolute, { recursive: true });
|
|
21842
23273
|
const rootStat = lstatSync2(absolute);
|
|
21843
23274
|
if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
|
|
@@ -21880,7 +23311,7 @@ function assertExistingSafeRun(root, runID) {
|
|
|
21880
23311
|
throw new LifecycleFsError("unavailable");
|
|
21881
23312
|
}
|
|
21882
23313
|
const child = relative3(root, real);
|
|
21883
|
-
if (child === "" || child.startsWith("..") ||
|
|
23314
|
+
if (child === "" || child.startsWith("..") || resolve12(root, child) !== real || dirname7(real) !== root) {
|
|
21884
23315
|
throw new LifecycleFsError("corrupt");
|
|
21885
23316
|
}
|
|
21886
23317
|
return real;
|
|
@@ -21929,7 +23360,7 @@ function appendDurable2(path4, content) {
|
|
|
21929
23360
|
}
|
|
21930
23361
|
}
|
|
21931
23362
|
function writeDurableAtomically(path4, content) {
|
|
21932
|
-
const temporary = join14(dirname7(path4), `.${basename3(path4)}.${
|
|
23363
|
+
const temporary = join14(dirname7(path4), `.${basename3(path4)}.${randomUUID3()}.next`);
|
|
21933
23364
|
try {
|
|
21934
23365
|
writeDurable2(temporary, content);
|
|
21935
23366
|
renameSync(temporary, path4);
|
|
@@ -21952,7 +23383,7 @@ function readLockContents(path4) {
|
|
|
21952
23383
|
}
|
|
21953
23384
|
function withRunMutationLock(runDir, mutate) {
|
|
21954
23385
|
const lockPath = join14(runDir, ".mutation-lock");
|
|
21955
|
-
const lockToken =
|
|
23386
|
+
const lockToken = randomUUID3();
|
|
21956
23387
|
let descriptor;
|
|
21957
23388
|
try {
|
|
21958
23389
|
descriptor = openSync2(lockPath, "wx");
|
|
@@ -22021,7 +23452,7 @@ function parseJson(text, parse2) {
|
|
|
22021
23452
|
}
|
|
22022
23453
|
}
|
|
22023
23454
|
function issueWriterToken() {
|
|
22024
|
-
return
|
|
23455
|
+
return randomUUID4().replaceAll("-", "");
|
|
22025
23456
|
}
|
|
22026
23457
|
function createLease(runID, writerToken, input) {
|
|
22027
23458
|
return parseLifecycleWriterLease({
|
|
@@ -22049,8 +23480,8 @@ function readRun(runDir, runID) {
|
|
|
22049
23480
|
if (metadataText === undefined || eventsText === undefined) {
|
|
22050
23481
|
throw new LifecycleFsError("corrupt");
|
|
22051
23482
|
}
|
|
22052
|
-
const
|
|
22053
|
-
if (
|
|
23483
|
+
const metadata3 = parseJson(metadataText, parseLifecycleRunMetadata);
|
|
23484
|
+
if (metadata3.runID !== runID) {
|
|
22054
23485
|
throw new LifecycleFsError("corrupt");
|
|
22055
23486
|
}
|
|
22056
23487
|
let decoded;
|
|
@@ -22065,10 +23496,10 @@ function readRun(runDir, runID) {
|
|
|
22065
23496
|
}
|
|
22066
23497
|
}
|
|
22067
23498
|
const first = decoded.events[0];
|
|
22068
|
-
if (first === undefined || first.type !== "run.started" || first.source !==
|
|
23499
|
+
if (first === undefined || first.type !== "run.started" || first.source !== metadata3.source || first.executionAuthority !== metadata3.executionAuthority || first.root.operationID !== metadata3.root.operationID || first.root.taskID !== metadata3.root.taskID) {
|
|
22069
23500
|
throw new LifecycleFsError("corrupt");
|
|
22070
23501
|
}
|
|
22071
|
-
return { metadata:
|
|
23502
|
+
return { metadata: metadata3, decoded };
|
|
22072
23503
|
}
|
|
22073
23504
|
function terminalOutcome(events) {
|
|
22074
23505
|
const last = events.at(-1);
|
|
@@ -22120,7 +23551,7 @@ function canDeleteTerminalRun(runDir, runID, now) {
|
|
|
22120
23551
|
return now >= lease.expiresAt;
|
|
22121
23552
|
}
|
|
22122
23553
|
var createFsLifecycleJournal = (options) => {
|
|
22123
|
-
const configuredRoot =
|
|
23554
|
+
const configuredRoot = resolve13(options.root);
|
|
22124
23555
|
const now = options.now ?? Date.now;
|
|
22125
23556
|
const mutateRun = options.withMutationLock ?? withRunMutationLock;
|
|
22126
23557
|
return {
|
|
@@ -22142,11 +23573,11 @@ var createFsLifecycleJournal = (options) => {
|
|
|
22142
23573
|
throw error;
|
|
22143
23574
|
}
|
|
22144
23575
|
}
|
|
22145
|
-
stagingPath = join15(root, `.start-${runID}-${
|
|
23576
|
+
stagingPath = join15(root, `.start-${runID}-${randomUUID4()}`);
|
|
22146
23577
|
mkdirSync3(stagingPath);
|
|
22147
23578
|
const writerToken = issueWriterToken();
|
|
22148
23579
|
const eventAt = now();
|
|
22149
|
-
const
|
|
23580
|
+
const metadata3 = parseLifecycleRunMetadata({
|
|
22150
23581
|
...parsedInput,
|
|
22151
23582
|
version: 1,
|
|
22152
23583
|
createdAt: eventAt
|
|
@@ -22162,7 +23593,7 @@ var createFsLifecycleJournal = (options) => {
|
|
|
22162
23593
|
executionAuthority: parsedInput.executionAuthority,
|
|
22163
23594
|
root: parsedInput.root
|
|
22164
23595
|
});
|
|
22165
|
-
writeDurable2(join15(stagingPath, METADATA_FILE), `${JSON.stringify(
|
|
23596
|
+
writeDurable2(join15(stagingPath, METADATA_FILE), `${JSON.stringify(metadata3)}
|
|
22166
23597
|
`);
|
|
22167
23598
|
writeDurable2(join15(stagingPath, EVENTS_FILE), encodeLifecycleJournal([event]));
|
|
22168
23599
|
writeDurable2(join15(stagingPath, OWNER_FILE2), `${JSON.stringify(lease)}
|
|
@@ -22178,7 +23609,7 @@ var createFsLifecycleJournal = (options) => {
|
|
|
22178
23609
|
stagingPath = undefined;
|
|
22179
23610
|
return ok({
|
|
22180
23611
|
handle: { runID, writerToken },
|
|
22181
|
-
metadata:
|
|
23612
|
+
metadata: metadata3,
|
|
22182
23613
|
event,
|
|
22183
23614
|
lease,
|
|
22184
23615
|
head: 1
|
|
@@ -22501,8 +23932,8 @@ async function initSpanEmit(connection, config) {
|
|
|
22501
23932
|
const tracer = provider.getTracer(DEFAULT_SERVICE_NAME);
|
|
22502
23933
|
return (record) => {
|
|
22503
23934
|
const span = tracer.startSpan(record.name);
|
|
22504
|
-
for (const [
|
|
22505
|
-
span.setAttribute(
|
|
23935
|
+
for (const [key3, value] of Object.entries(record.attributes)) {
|
|
23936
|
+
span.setAttribute(key3, value);
|
|
22506
23937
|
}
|
|
22507
23938
|
span.end();
|
|
22508
23939
|
};
|
|
@@ -22620,7 +24051,8 @@ var RELEASE_CERT_PATH = "artifacts/graph-release-certificate.json";
|
|
|
22620
24051
|
var SOAK_CERT_PATH = "artifacts/graph-soak-certificate.json";
|
|
22621
24052
|
async function readPackagedCertificate(path4) {
|
|
22622
24053
|
try {
|
|
22623
|
-
|
|
24054
|
+
const hostDirectory = basename4(dirname8(path4)) === NATIVE_CERTIFICATE_DIRECTORY ? `${NATIVE_CERTIFICATE_DIRECTORY}/` : "";
|
|
24055
|
+
return await readFile5(new URL(`./certificates/${hostDirectory}${basename4(path4)}`, import.meta.url), "utf8");
|
|
22624
24056
|
} catch {
|
|
22625
24057
|
return;
|
|
22626
24058
|
}
|
|
@@ -22752,8 +24184,8 @@ async function readOpencodeVersion(serverUrl, options = {}) {
|
|
|
22752
24184
|
}
|
|
22753
24185
|
var DEFAULT_OPENCODE_VERSION_BACKOFF_MS = [50, 250];
|
|
22754
24186
|
function defaultSleep(ms) {
|
|
22755
|
-
return new Promise((
|
|
22756
|
-
setTimeout(
|
|
24187
|
+
return new Promise((resolve14) => {
|
|
24188
|
+
setTimeout(resolve14, ms);
|
|
22757
24189
|
});
|
|
22758
24190
|
}
|
|
22759
24191
|
function createLazyOpencodeVersionReader(serverUrl, options = {}) {
|
|
@@ -22796,9 +24228,10 @@ function createLazyOpencodeVersionReader(serverUrl, options = {}) {
|
|
|
22796
24228
|
};
|
|
22797
24229
|
}
|
|
22798
24230
|
async function buildGateInput(config, storage, opencodeVersion, warn, packagedRead = readPackagedCertificate) {
|
|
22799
|
-
const
|
|
24231
|
+
const hostPath = (path4) => opencodeVersion === NATIVE_OPENCODE_VERSION ? `${dirname8(path4)}/${NATIVE_CERTIFICATE_DIRECTORY}/${basename4(path4)}` : path4;
|
|
24232
|
+
const shadow = await readAndParseCertificate(storage, hostPath(SHADOW_CERT_PATH), (raw) => parseShadowCertificate(raw, opencodeVersion), (cert) => shadowCertificateBindingDigest(cert.evidence), packagedRead);
|
|
22800
24233
|
const shadowDigest = shadow.status === "valid" ? shadow.digest : "0".repeat(64);
|
|
22801
|
-
const release = await readAndParseCertificate(storage, RELEASE_CERT_PATH, (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest, packagedRead);
|
|
24234
|
+
const release = await readAndParseCertificate(storage, hostPath(RELEASE_CERT_PATH), (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest, packagedRead);
|
|
22802
24235
|
let soakLedgerDigest = "0".repeat(64);
|
|
22803
24236
|
try {
|
|
22804
24237
|
const recorderPorts = recorderPortsFromStorage(storage);
|
|
@@ -23053,14 +24486,17 @@ async function listConfiguredOpencodeInventory(ctx) {
|
|
|
23053
24486
|
profiles: normalizeModelsDevCatalog(Object.fromEntries(providers.map((provider) => [provider.id, provider]))).filter((profile) => profile.costPer1M.inputUSD > 0 && profile.costPer1M.outputUSD > 0)
|
|
23054
24487
|
};
|
|
23055
24488
|
}
|
|
23056
|
-
|
|
24489
|
+
async function createPluginRuntime(ctx, rawOptions) {
|
|
23057
24490
|
const now = () => Date.now();
|
|
23058
24491
|
const diagnostics = createPluginDiagnosticChannel({
|
|
23059
24492
|
client: ctx.client,
|
|
23060
24493
|
directory: ctx.directory,
|
|
23061
24494
|
now
|
|
23062
24495
|
});
|
|
23063
|
-
|
|
24496
|
+
const initialServerUrl = ctx.serverUrl();
|
|
24497
|
+
if (initialServerUrl !== undefined) {
|
|
24498
|
+
logOpencodeServerUrl(initialServerUrl, diagnostics.info("opencode-server-url", { persist: false, toast: false }));
|
|
24499
|
+
}
|
|
23064
24500
|
const storage = createFsStorageProvider(ctx.directory || ctx.worktree || process.cwd());
|
|
23065
24501
|
const runtimeConfig = await loadRuntimeOpenTeamConfig(storage, rawOptions);
|
|
23066
24502
|
const config = runtimeConfig.config;
|
|
@@ -23069,20 +24505,19 @@ var server = async (ctx, rawOptions) => {
|
|
|
23069
24505
|
const registry = new RuntimeRegistry({
|
|
23070
24506
|
fetch: globalThis.fetch,
|
|
23071
24507
|
clock: () => new Date().toISOString(),
|
|
23072
|
-
foundryDiscovery: foundryDiscoveryFromExec(
|
|
24508
|
+
foundryDiscovery: foundryDiscoveryFromExec(ctx.exec)
|
|
23073
24509
|
});
|
|
23074
24510
|
const cache = createAvailabilityCache({
|
|
23075
24511
|
config,
|
|
23076
24512
|
registry,
|
|
23077
|
-
...ctx.
|
|
23078
|
-
listConfiguredInventory: () => listConfiguredOpencodeInventory(ctx)
|
|
23079
|
-
},
|
|
24513
|
+
...ctx.listConfiguredInventory === undefined ? {} : { listConfiguredInventory: ctx.listConfiguredInventory },
|
|
23080
24514
|
onRefreshError: (error) => logAvailabilityRefreshError(error, diagnostics.warn("availability-refresh-failed", { toast: false }))
|
|
23081
24515
|
});
|
|
23082
24516
|
let availabilityReady;
|
|
23083
|
-
const ensureAvailability = () => {
|
|
24517
|
+
const ensureAvailability = async () => {
|
|
23084
24518
|
availabilityReady ??= cache.refresh();
|
|
23085
|
-
|
|
24519
|
+
await availabilityReady;
|
|
24520
|
+
await cache.waitForRefresh();
|
|
23086
24521
|
};
|
|
23087
24522
|
const capabilityProfiles = () => {
|
|
23088
24523
|
const availableModelKeys = new Set(cache.get().filter((model) => model.available).map((model) => `${model.providerID}\x00${model.modelID}`));
|
|
@@ -23111,17 +24546,19 @@ var server = async (ctx, rawOptions) => {
|
|
|
23111
24546
|
const injectMemory = await buildMemoryInjectorFromConfig(config, storage, diagnostics.warn("memory-runtime-warning", { toast: false }));
|
|
23112
24547
|
const sessionTracker = createSessionTracker();
|
|
23113
24548
|
const orchestratedModelOwnership = createOrchestratedModelOwnership();
|
|
23114
|
-
const
|
|
24549
|
+
const resolveAgentRole = async (agentName) => {
|
|
24550
|
+
const entry = (await readRoutingRosterEntries(storage)).find((candidate) => candidate.agentName === agentName);
|
|
24551
|
+
return entry?.roleID;
|
|
24552
|
+
};
|
|
24553
|
+
const residencyOptions = {
|
|
23115
24554
|
fetch: globalThis.fetch,
|
|
23116
|
-
|
|
24555
|
+
resolveAgentRole,
|
|
24556
|
+
...ctx.listConfiguredInventory === undefined ? {} : {
|
|
23117
24557
|
resolveApiModelID: cache.resolveApiModelID,
|
|
23118
24558
|
modelInventoryReady: ensureAvailability
|
|
23119
24559
|
}
|
|
23120
|
-
});
|
|
23121
|
-
const resolveAgentRole = async (agentName) => {
|
|
23122
|
-
const entry = (await readRoutingRosterEntries(storage)).find((candidate) => candidate.agentName === agentName);
|
|
23123
|
-
return entry?.roleID;
|
|
23124
24560
|
};
|
|
24561
|
+
const modelResidency = (ctx.createResidencyGuard ?? createModelResidencyGuard)(config, residencyOptions);
|
|
23125
24562
|
const hooks = createHooks(config, cache.get, injectMemory === undefined ? {
|
|
23126
24563
|
sink,
|
|
23127
24564
|
modelResidency,
|
|
@@ -23140,10 +24577,12 @@ var server = async (ctx, rawOptions) => {
|
|
|
23140
24577
|
resolveAgentRole,
|
|
23141
24578
|
onRoute: (sid, model) => sessionTracker.trackRoutedModel(sid, model)
|
|
23142
24579
|
});
|
|
23143
|
-
const getOpencodeVersion =
|
|
24580
|
+
const getOpencodeVersion = ctx.opencodeVersion ?? (initialServerUrl === undefined ? async () => {
|
|
24581
|
+
throw new Error(nativeHostMessages.missingVersion);
|
|
24582
|
+
} : createLazyOpencodeVersionReader(initialServerUrl, {
|
|
23144
24583
|
client: ctx.client,
|
|
23145
24584
|
onFailure: createOpencodeVersionReadFailureLogger(MAX_LOGGED_OPENCODE_VERSION_FAILURE_CAUSES, diagnostics.warn("opencode-version-read-failed", { toast: false }))
|
|
23146
|
-
});
|
|
24585
|
+
}));
|
|
23147
24586
|
const cliDeps = createCliDeps(config, registry, telemetryPath, sessionsDir, storage, runtimeConfig.source, rawOptions, async () => {
|
|
23148
24587
|
const version = await getOpencodeVersion();
|
|
23149
24588
|
return version === "unknown" ? undefined : version;
|
|
@@ -23154,12 +24593,15 @@ var server = async (ctx, rawOptions) => {
|
|
|
23154
24593
|
if (sessionID === undefined || announcedSessions.has(sessionID)) {
|
|
23155
24594
|
return;
|
|
23156
24595
|
}
|
|
23157
|
-
|
|
23158
|
-
|
|
24596
|
+
const serverUrl = ctx.serverUrl();
|
|
24597
|
+
if (serverUrl === undefined)
|
|
24598
|
+
return;
|
|
24599
|
+
const endpoint = sessionEndpointEventFrom(sessionID, serverUrl, now, {
|
|
23159
24600
|
directory: ctx.directory,
|
|
23160
24601
|
worktree: ctx.worktree
|
|
23161
24602
|
});
|
|
23162
24603
|
if (endpoint !== undefined) {
|
|
24604
|
+
announcedSessions.add(sessionID);
|
|
23163
24605
|
sink.emit(endpoint);
|
|
23164
24606
|
}
|
|
23165
24607
|
};
|
|
@@ -23211,7 +24653,7 @@ var server = async (ctx, rawOptions) => {
|
|
|
23211
24653
|
holder: "openteam-plugin",
|
|
23212
24654
|
lifecycleSource: "plugin",
|
|
23213
24655
|
lifecycleRecorder,
|
|
23214
|
-
exec:
|
|
24656
|
+
exec: ctx.exec,
|
|
23215
24657
|
repoRoot: ctx.directory
|
|
23216
24658
|
});
|
|
23217
24659
|
const graphSurface = createGraphTool({
|
|
@@ -23247,8 +24689,8 @@ var server = async (ctx, rawOptions) => {
|
|
|
23247
24689
|
rememberReportedSlots(snapshots);
|
|
23248
24690
|
return reachableRuntimeIds(snapshots);
|
|
23249
24691
|
};
|
|
23250
|
-
const runtimeById = (
|
|
23251
|
-
const runtimeId = slotPoolRuntimeID(
|
|
24692
|
+
const runtimeById = (key3) => {
|
|
24693
|
+
const runtimeId = slotPoolRuntimeID(key3);
|
|
23252
24694
|
const runtime = config.local.runtimes.find((candidate) => candidate.id === runtimeId);
|
|
23253
24695
|
if (runtime === undefined) {
|
|
23254
24696
|
return;
|
|
@@ -23256,13 +24698,13 @@ var server = async (ctx, rawOptions) => {
|
|
|
23256
24698
|
return withReportedModelSlots(runtime, reportedSlotsByRuntime.get(runtimeId), localModelAliases(runtime.defaultModel.providerID, cache.get()));
|
|
23257
24699
|
};
|
|
23258
24700
|
const runtimeLimiter = createRuntimeConcurrencyLimiter({
|
|
23259
|
-
capacityFor: (
|
|
23260
|
-
const runtime = runtimeById(
|
|
23261
|
-
return runtime === undefined ? unknownPoolCapacity() : slotPoolCapacity(runtime, slotPoolModelID(
|
|
24701
|
+
capacityFor: (key3) => {
|
|
24702
|
+
const runtime = runtimeById(key3);
|
|
24703
|
+
return runtime === undefined ? unknownPoolCapacity() : slotPoolCapacity(runtime, slotPoolModelID(key3));
|
|
23262
24704
|
},
|
|
23263
|
-
reservedInteractiveFor: (
|
|
23264
|
-
const runtime = runtimeById(
|
|
23265
|
-
return runtime === undefined ? 0 : slotPoolReservedInteractive(runtime, slotPoolModelID(
|
|
24705
|
+
reservedInteractiveFor: (key3) => {
|
|
24706
|
+
const runtime = runtimeById(key3);
|
|
24707
|
+
return runtime === undefined ? 0 : slotPoolReservedInteractive(runtime, slotPoolModelID(key3));
|
|
23266
24708
|
},
|
|
23267
24709
|
now
|
|
23268
24710
|
});
|
|
@@ -23289,8 +24731,8 @@ var server = async (ctx, rawOptions) => {
|
|
|
23289
24731
|
lifecycleRecorder,
|
|
23290
24732
|
abortGraceMs: DEFAULT_ABORT_GRACE_MS,
|
|
23291
24733
|
statusPollMs: DEFAULT_STATUS_POLL_MS,
|
|
23292
|
-
delay: (ms) => new Promise((
|
|
23293
|
-
setTimeout(
|
|
24734
|
+
delay: (ms) => new Promise((resolve14) => {
|
|
24735
|
+
setTimeout(resolve14, ms);
|
|
23294
24736
|
}),
|
|
23295
24737
|
warn: diagnostics.warn("unknown-role")
|
|
23296
24738
|
});
|
|
@@ -23299,8 +24741,22 @@ var server = async (ctx, rawOptions) => {
|
|
|
23299
24741
|
storage,
|
|
23300
24742
|
now,
|
|
23301
24743
|
localRuntimeReachable: async (runtime, signal) => {
|
|
24744
|
+
if (localRuntimeKind(runtime) === "llama-swap") {
|
|
24745
|
+
if (ctx.listConfiguredInventory !== undefined)
|
|
24746
|
+
await ensureAvailability();
|
|
24747
|
+
signal?.throwIfAborted();
|
|
24748
|
+
const target = modelResidency.localTarget(resolveMemorySemantic(config).extraction.model ?? runtime.defaultModel);
|
|
24749
|
+
const residency = await readLlamaSwapResidency(target.baseURL, globalThis.fetch, {
|
|
24750
|
+
...signal === undefined ? {} : { signal }
|
|
24751
|
+
});
|
|
24752
|
+
if (!residency.has(target.modelID))
|
|
24753
|
+
throw new ModelResidencyError("identity", "llama-swap");
|
|
24754
|
+
if (residency.get(target.modelID)?.loaded !== true)
|
|
24755
|
+
throw new ModelResidencyError("inactive", "llama-swap");
|
|
24756
|
+
return true;
|
|
24757
|
+
}
|
|
23302
24758
|
if (localRuntimeKind(runtime) === "lemonade") {
|
|
23303
|
-
if (ctx.
|
|
24759
|
+
if (ctx.listConfiguredInventory !== undefined)
|
|
23304
24760
|
await ensureAvailability();
|
|
23305
24761
|
signal?.throwIfAborted();
|
|
23306
24762
|
const target = modelResidency.localTarget(resolveMemorySemantic(config).extraction.model ?? runtime.defaultModel);
|
|
@@ -23315,7 +24771,7 @@ var server = async (ctx, rawOptions) => {
|
|
|
23315
24771
|
return anyRuntimeReachable(snapshots);
|
|
23316
24772
|
},
|
|
23317
24773
|
resolveExtractionTarget: async (model) => {
|
|
23318
|
-
if (ctx.
|
|
24774
|
+
if (ctx.listConfiguredInventory !== undefined)
|
|
23319
24775
|
await ensureAvailability();
|
|
23320
24776
|
return modelResidency.localTarget(model);
|
|
23321
24777
|
},
|
|
@@ -23343,6 +24799,7 @@ var server = async (ctx, rawOptions) => {
|
|
|
23343
24799
|
"chat.message": async (input, output) => {
|
|
23344
24800
|
reload.userTurn(input.sessionID);
|
|
23345
24801
|
await hooks["chat.message"]?.(input, output);
|
|
24802
|
+
announceEndpoint(input.sessionID);
|
|
23346
24803
|
},
|
|
23347
24804
|
dispose: async () => {
|
|
23348
24805
|
reload.dispose();
|
|
@@ -23350,7 +24807,7 @@ var server = async (ctx, rawOptions) => {
|
|
|
23350
24807
|
},
|
|
23351
24808
|
tool: {
|
|
23352
24809
|
openteam: createCommandTool(cliDeps),
|
|
23353
|
-
"openteam-console": createConsoleTool(),
|
|
24810
|
+
"openteam-console": createConsoleTool(ctx.consoleEnvironment === undefined ? {} : { environment: ctx.consoleEnvironment }),
|
|
23354
24811
|
"openteam-graph": graphSurface.definition,
|
|
23355
24812
|
"openteam-orchestrate": orchestrateTool,
|
|
23356
24813
|
"openteam-memory": memoryTool,
|
|
@@ -23387,10 +24844,21 @@ var server = async (ctx, rawOptions) => {
|
|
|
23387
24844
|
}
|
|
23388
24845
|
}
|
|
23389
24846
|
};
|
|
23390
|
-
}
|
|
24847
|
+
}
|
|
24848
|
+
var server = (ctx, rawOptions) => createPluginRuntime({
|
|
24849
|
+
directory: ctx.directory,
|
|
24850
|
+
worktree: ctx.worktree,
|
|
24851
|
+
client: ctx.client,
|
|
24852
|
+
exec: createShellExec(ctx.$),
|
|
24853
|
+
serverUrl: () => ctx.serverUrl,
|
|
24854
|
+
...ctx.client.config?.providers === undefined ? {} : {
|
|
24855
|
+
listConfiguredInventory: () => listConfiguredOpencodeInventory(ctx)
|
|
24856
|
+
}
|
|
24857
|
+
}, rawOptions);
|
|
23391
24858
|
var plugin = {
|
|
23392
24859
|
id: "openteam",
|
|
23393
|
-
server
|
|
24860
|
+
server,
|
|
24861
|
+
effect: createNativePluginEffect(createPluginRuntime)
|
|
23394
24862
|
};
|
|
23395
24863
|
var src_default = plugin;
|
|
23396
24864
|
export {
|
|
@@ -23408,6 +24876,7 @@ export {
|
|
|
23408
24876
|
isMissingFile2 as isMissingFile,
|
|
23409
24877
|
evaluateCutoverGateForBoundary,
|
|
23410
24878
|
src_default as default,
|
|
24879
|
+
createPluginRuntime,
|
|
23411
24880
|
createOpencodeVersionReadFailureLogger,
|
|
23412
24881
|
createLazyOpencodeVersionReader,
|
|
23413
24882
|
createEventSink,
|