@algosuite/vo-mcp 0.2.0-beta.20 → 0.2.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +180 -15
- package/dist/cli.js.map +4 -4
- package/dist/index.js +186 -34
- package/dist/index.js.map +4 -4
- package/dist/runner-cli.js +569 -11
- package/dist/runner-cli.js.map +4 -4
- package/dist/runner-supervisor.js +4 -1
- package/dist/runner-supervisor.js.map +2 -2
- package/dist/set-key-cli.js +8 -2
- package/dist/set-key-cli.js.map +2 -2
- package/package.json +1 -1
package/dist/runner-cli.js
CHANGED
|
@@ -2477,7 +2477,7 @@ function createControlPlaneClient({
|
|
|
2477
2477
|
* authenticated operator so the web shows a TRUE "runner online" signal.
|
|
2478
2478
|
* Best-effort caller; throws on 401/non-ok so the daemon can log + retry.
|
|
2479
2479
|
*/
|
|
2480
|
-
async postHeartbeat({ runnerId, runnerInstanceId, operatorId, uptimeSec, activeTasks, maxConcurrency, effectiveConcurrency, measuredTaskSlots, measuredCpuSlots, measuredMemorySlots, version, daemonVersion, defaultAgent, supervisorInstanceId, supervisorVersion, supervisorCapabilities, servedRepos, servedOperators, availableAgents, accountUsage }) {
|
|
2480
|
+
async postHeartbeat({ runnerId, runnerInstanceId, operatorId, uptimeSec, activeTasks, maxConcurrency, effectiveConcurrency, measuredTaskSlots, measuredCpuSlots, measuredMemorySlots, version, daemonVersion, defaultAgent, supervisorInstanceId, supervisorVersion, supervisorCapabilities, servedRepos, servedOperators, availableAgents, accountUsage, availableLocalModels }) {
|
|
2481
2481
|
const body = { runner_id: runnerId };
|
|
2482
2482
|
if (runnerInstanceId) body.runner_instance_id = runnerInstanceId;
|
|
2483
2483
|
if (operatorId) body.operator_id = operatorId;
|
|
@@ -2506,6 +2506,9 @@ function createControlPlaneClient({
|
|
|
2506
2506
|
if (Array.isArray(accountUsage) && accountUsage.length > 0) {
|
|
2507
2507
|
body.account_usage = accountUsage;
|
|
2508
2508
|
}
|
|
2509
|
+
if (Array.isArray(availableLocalModels) && availableLocalModels.length > 0) {
|
|
2510
|
+
body.available_local_models = availableLocalModels;
|
|
2511
|
+
}
|
|
2509
2512
|
const res = await req("POST", "/api/v1/runner/heartbeat", body, {
|
|
2510
2513
|
timeoutMs: heartbeatTimeoutMs
|
|
2511
2514
|
});
|
|
@@ -3752,7 +3755,10 @@ var init_agent_key_store = __esm({
|
|
|
3752
3755
|
meta: ["MODEL_API_KEY"],
|
|
3753
3756
|
// Generic OpenAI-compatible runner (bring-your-own model + endpoint): its key
|
|
3754
3757
|
// is a dedicated var so it never collides with a real OpenAI/Codex key.
|
|
3755
|
-
"oai-compat": ["VO_CODE_RUNNER_OAI_API_KEY"]
|
|
3758
|
+
"oai-compat": ["VO_CODE_RUNNER_OAI_API_KEY"],
|
|
3759
|
+
// Sovereign local inference (Ollama / LM Studio). The key is OPTIONAL — most
|
|
3760
|
+
// local servers need none — and exists for locally secured endpoints only.
|
|
3761
|
+
local: ["VO_CODE_RUNNER_LOCAL_API_KEY"]
|
|
3756
3762
|
};
|
|
3757
3763
|
PROVIDER_ALIAS = {
|
|
3758
3764
|
claude: "anthropic",
|
|
@@ -3765,7 +3771,10 @@ var init_agent_key_store = __esm({
|
|
|
3765
3771
|
spark: "meta",
|
|
3766
3772
|
"muse-spark": "meta",
|
|
3767
3773
|
oai: "oai-compat",
|
|
3768
|
-
"oai-compat": "oai-compat"
|
|
3774
|
+
"oai-compat": "oai-compat",
|
|
3775
|
+
local: "local",
|
|
3776
|
+
ollama: "local",
|
|
3777
|
+
lmstudio: "local"
|
|
3769
3778
|
};
|
|
3770
3779
|
_loadTried2 = false;
|
|
3771
3780
|
}
|
|
@@ -4096,6 +4105,200 @@ var init_cursor_runner = __esm({
|
|
|
4096
4105
|
}
|
|
4097
4106
|
});
|
|
4098
4107
|
|
|
4108
|
+
// ../../scripts/virtual-office/code-runner/local-model-runner.mjs
|
|
4109
|
+
function resolveLocalProvider(env2 = process.env) {
|
|
4110
|
+
return String(env2.VO_CODE_RUNNER_LOCAL_PROVIDER || "").trim().toLowerCase() || DEFAULT_LOCAL_PROVIDER;
|
|
4111
|
+
}
|
|
4112
|
+
function setRemoteDesiredLocalModel(model) {
|
|
4113
|
+
const value = typeof model === "string" ? model.trim() : "";
|
|
4114
|
+
remoteDesiredLocalModel = value && isValidLocalModel(value) ? value : "";
|
|
4115
|
+
}
|
|
4116
|
+
function resolveLocalModel(env2 = process.env) {
|
|
4117
|
+
return String(env2.VO_CODE_RUNNER_LOCAL_MODEL || "").trim() || remoteDesiredLocalModel;
|
|
4118
|
+
}
|
|
4119
|
+
function resolveLocalBaseUrl(env2 = process.env) {
|
|
4120
|
+
return String(env2.VO_CODE_RUNNER_LOCAL_BASE_URL || "").trim();
|
|
4121
|
+
}
|
|
4122
|
+
function isValidLocalModel(model) {
|
|
4123
|
+
return LOCAL_MODEL_RE.test(String(model || ""));
|
|
4124
|
+
}
|
|
4125
|
+
function isLoopbackBaseUrl(url) {
|
|
4126
|
+
const raw = String(url || "").trim();
|
|
4127
|
+
if (!/^https?:\/\/[^\s"'`\\]+$/.test(raw)) return false;
|
|
4128
|
+
let parsed;
|
|
4129
|
+
try {
|
|
4130
|
+
parsed = new URL(raw);
|
|
4131
|
+
} catch {
|
|
4132
|
+
return false;
|
|
4133
|
+
}
|
|
4134
|
+
const host = parsed.hostname.toLowerCase();
|
|
4135
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
4136
|
+
}
|
|
4137
|
+
function buildLocalArgs(opts = {}, env2 = process.env) {
|
|
4138
|
+
const provider = resolveLocalProvider(env2);
|
|
4139
|
+
if (!LOCAL_PROVIDERS.includes(provider)) {
|
|
4140
|
+
throw new Error(
|
|
4141
|
+
`local-model runner: unknown VO_CODE_RUNNER_LOCAL_PROVIDER "${provider}" (supported: ${LOCAL_PROVIDERS.join(", ")}).`
|
|
4142
|
+
);
|
|
4143
|
+
}
|
|
4144
|
+
const model = resolveLocalModel(env2);
|
|
4145
|
+
if (!model) {
|
|
4146
|
+
throw new Error(
|
|
4147
|
+
"local-model runner: set VO_CODE_RUNNER_LOCAL_MODEL to a model your local server already has (recommended: gpt-oss:20b \u2014 codex drives its tool loop reliably; generic chat models often cannot edit files agentically), or pick an already-pulled model from the web runner settings. Refusing to run with no explicit model (fail-closed)."
|
|
4148
|
+
);
|
|
4149
|
+
}
|
|
4150
|
+
if (!isValidLocalModel(model)) {
|
|
4151
|
+
throw new Error(`local-model runner: "${model}" is not a valid local model id.`);
|
|
4152
|
+
}
|
|
4153
|
+
const baseUrl = resolveLocalBaseUrl(env2);
|
|
4154
|
+
if (baseUrl && !isLoopbackBaseUrl(baseUrl)) {
|
|
4155
|
+
throw new Error(
|
|
4156
|
+
"local-model runner: VO_CODE_RUNNER_LOCAL_BASE_URL must be a loopback http(s) URL (localhost / 127.0.0.1 / [::1]). Remote endpoints are refused \u2014 use the Model Firewall lanes for hosted providers."
|
|
4157
|
+
);
|
|
4158
|
+
}
|
|
4159
|
+
const args = [
|
|
4160
|
+
"exec",
|
|
4161
|
+
"--json",
|
|
4162
|
+
"-c",
|
|
4163
|
+
'approval_policy="never"',
|
|
4164
|
+
"--sandbox",
|
|
4165
|
+
"workspace-write",
|
|
4166
|
+
"--skip-git-repo-check",
|
|
4167
|
+
"--oss",
|
|
4168
|
+
"--local-provider",
|
|
4169
|
+
provider,
|
|
4170
|
+
"--model",
|
|
4171
|
+
model
|
|
4172
|
+
];
|
|
4173
|
+
if (opts.effort) {
|
|
4174
|
+
args.push("-c", `model_reasoning_effort="${String(opts.effort)}"`);
|
|
4175
|
+
}
|
|
4176
|
+
args.push("-");
|
|
4177
|
+
return args;
|
|
4178
|
+
}
|
|
4179
|
+
function applyLocalAuthEnv(baseEnv = process.env, configEnv = process.env) {
|
|
4180
|
+
const out = withAgentKey("local", baseEnv);
|
|
4181
|
+
const baseUrl = resolveLocalBaseUrl(configEnv);
|
|
4182
|
+
if (baseUrl && isLoopbackBaseUrl(baseUrl) && resolveLocalProvider(configEnv) === "ollama" && !String(out.OLLAMA_HOST || "").trim()) {
|
|
4183
|
+
out.OLLAMA_HOST = baseUrl.replace(/\/+$/, "");
|
|
4184
|
+
}
|
|
4185
|
+
return out;
|
|
4186
|
+
}
|
|
4187
|
+
var LOCAL_API_KEY_ENV, LOCAL_PROVIDERS, DEFAULT_LOCAL_PROVIDER, LOCAL_PROBE_URLS, remoteDesiredLocalModel, LOCAL_MODEL_RE, LocalModelRunner, localModelRunner;
|
|
4188
|
+
var init_local_model_runner = __esm({
|
|
4189
|
+
"../../scripts/virtual-office/code-runner/local-model-runner.mjs"() {
|
|
4190
|
+
"use strict";
|
|
4191
|
+
init_codex_runner();
|
|
4192
|
+
init_agent_key_store();
|
|
4193
|
+
LOCAL_API_KEY_ENV = "VO_CODE_RUNNER_LOCAL_API_KEY";
|
|
4194
|
+
LOCAL_PROVIDERS = ["ollama", "lmstudio"];
|
|
4195
|
+
DEFAULT_LOCAL_PROVIDER = "ollama";
|
|
4196
|
+
LOCAL_PROBE_URLS = {
|
|
4197
|
+
ollama: "http://127.0.0.1:11434/api/version",
|
|
4198
|
+
lmstudio: "http://127.0.0.1:1234/v1/models"
|
|
4199
|
+
};
|
|
4200
|
+
remoteDesiredLocalModel = "";
|
|
4201
|
+
LOCAL_MODEL_RE = new RegExp("^[A-Za-z0-9][A-Za-z0-9._/:-]{0,127}$");
|
|
4202
|
+
LocalModelRunner = class {
|
|
4203
|
+
constructor({
|
|
4204
|
+
spawn: spawn5 = null,
|
|
4205
|
+
resolveBinary = resolveCodexBinary,
|
|
4206
|
+
env: env2 = process.env,
|
|
4207
|
+
fetchImpl = globalThis.fetch
|
|
4208
|
+
} = {}) {
|
|
4209
|
+
this.spawn = spawn5;
|
|
4210
|
+
this.resolveBinary = resolveBinary;
|
|
4211
|
+
this.env = env2;
|
|
4212
|
+
this.fetchImpl = fetchImpl;
|
|
4213
|
+
}
|
|
4214
|
+
/** Codex is the transport binary; the model/endpoint are the user's. */
|
|
4215
|
+
get binary() {
|
|
4216
|
+
return this.resolveBinary();
|
|
4217
|
+
}
|
|
4218
|
+
buildArgs(opts = {}) {
|
|
4219
|
+
return buildLocalArgs(opts, this.env);
|
|
4220
|
+
}
|
|
4221
|
+
/** Codex JSONL events map identically → reuse the proven parser. */
|
|
4222
|
+
parseEvent(line) {
|
|
4223
|
+
return parseCodexEvent(line);
|
|
4224
|
+
}
|
|
4225
|
+
// SECURITY: never shell — see no-shell-spawn.test.mjs. The model id is
|
|
4226
|
+
// control-plane-influenced and validated, but shell:false is the hard floor.
|
|
4227
|
+
getSpawnOptions() {
|
|
4228
|
+
return {
|
|
4229
|
+
shell: false,
|
|
4230
|
+
windowsHide: true,
|
|
4231
|
+
windowsVerbatimArguments: false
|
|
4232
|
+
};
|
|
4233
|
+
}
|
|
4234
|
+
applyAuthEnv(env2 = process.env) {
|
|
4235
|
+
return applyLocalAuthEnv(env2, this.env);
|
|
4236
|
+
}
|
|
4237
|
+
describeAuth(env2 = process.env) {
|
|
4238
|
+
const authEnv = this.applyAuthEnv(env2);
|
|
4239
|
+
const provider = resolveLocalProvider(this.env);
|
|
4240
|
+
const model = resolveLocalModel(this.env) || "<unset>";
|
|
4241
|
+
const hasKey = Boolean(String(authEnv[LOCAL_API_KEY_ENV] || "").trim());
|
|
4242
|
+
return `local provider=${provider} model=${model} key=${hasKey ? "set" : "none (optional)"}`;
|
|
4243
|
+
}
|
|
4244
|
+
/**
|
|
4245
|
+
* Best-effort: codex transport present AND the local inference endpoint
|
|
4246
|
+
* answers. Never throws, never spends tokens; the endpoint probe is bounded
|
|
4247
|
+
* to 1.5s so a stopped Ollama can't hang availability checks.
|
|
4248
|
+
*/
|
|
4249
|
+
async checkAuth() {
|
|
4250
|
+
const provider = resolveLocalProvider(this.env);
|
|
4251
|
+
if (!LOCAL_PROVIDERS.includes(provider)) {
|
|
4252
|
+
return {
|
|
4253
|
+
installed: false,
|
|
4254
|
+
authenticated: false,
|
|
4255
|
+
message: `unknown local provider "${provider}" (supported: ${LOCAL_PROVIDERS.join(", ")})`
|
|
4256
|
+
};
|
|
4257
|
+
}
|
|
4258
|
+
const model = resolveLocalModel(this.env);
|
|
4259
|
+
const override = resolveLocalBaseUrl(this.env);
|
|
4260
|
+
if (override && !isLoopbackBaseUrl(override)) {
|
|
4261
|
+
return {
|
|
4262
|
+
installed: true,
|
|
4263
|
+
authenticated: false,
|
|
4264
|
+
message: "VO_CODE_RUNNER_LOCAL_BASE_URL is not loopback \u2014 refused (fail-closed)"
|
|
4265
|
+
};
|
|
4266
|
+
}
|
|
4267
|
+
const probeUrl = provider === "ollama" && override ? `${override.replace(/\/+$/, "")}/api/version` : LOCAL_PROBE_URLS[provider];
|
|
4268
|
+
let endpointUp = false;
|
|
4269
|
+
let probeNote = "";
|
|
4270
|
+
try {
|
|
4271
|
+
const res = await this.fetchImpl(probeUrl, { signal: AbortSignal.timeout(1500) });
|
|
4272
|
+
endpointUp = Boolean(res?.ok);
|
|
4273
|
+
if (!endpointUp) probeNote = `endpoint ${probeUrl} answered HTTP ${res?.status}`;
|
|
4274
|
+
} catch {
|
|
4275
|
+
probeNote = `no local inference server answering at ${probeUrl}`;
|
|
4276
|
+
}
|
|
4277
|
+
if (!endpointUp) {
|
|
4278
|
+
return {
|
|
4279
|
+
installed: true,
|
|
4280
|
+
authenticated: false,
|
|
4281
|
+
message: `${probeNote} \u2014 start ${provider === "ollama" ? "Ollama" : "LM Studio"} first`
|
|
4282
|
+
};
|
|
4283
|
+
}
|
|
4284
|
+
if (!model) {
|
|
4285
|
+
return {
|
|
4286
|
+
installed: true,
|
|
4287
|
+
authenticated: false,
|
|
4288
|
+
message: `${provider} is running but VO_CODE_RUNNER_LOCAL_MODEL is not set`
|
|
4289
|
+
};
|
|
4290
|
+
}
|
|
4291
|
+
return {
|
|
4292
|
+
installed: true,
|
|
4293
|
+
authenticated: true,
|
|
4294
|
+
message: `${provider} reachable; model "${model}" configured (local-only, no cloud spend)`
|
|
4295
|
+
};
|
|
4296
|
+
}
|
|
4297
|
+
};
|
|
4298
|
+
localModelRunner = new LocalModelRunner();
|
|
4299
|
+
}
|
|
4300
|
+
});
|
|
4301
|
+
|
|
4099
4302
|
// ../../scripts/virtual-office/code-runner/meta-runner.mjs
|
|
4100
4303
|
function applyMetaAuthEnv(baseEnv = process.env) {
|
|
4101
4304
|
const out = withAgentKey("meta", baseEnv);
|
|
@@ -4311,6 +4514,7 @@ var init_resolve_runner = __esm({
|
|
|
4311
4514
|
init_claude_runner();
|
|
4312
4515
|
init_codex_runner();
|
|
4313
4516
|
init_cursor_runner();
|
|
4517
|
+
init_local_model_runner();
|
|
4314
4518
|
init_meta_runner();
|
|
4315
4519
|
init_openai_compatible_runner();
|
|
4316
4520
|
init_agent_runner_interface();
|
|
@@ -4319,6 +4523,9 @@ var init_resolve_runner = __esm({
|
|
|
4319
4523
|
claude: claudeRunner,
|
|
4320
4524
|
codex: codexRunner,
|
|
4321
4525
|
cursor: cursorRunner,
|
|
4526
|
+
// `local` = sovereign local inference (Ollama / LM Studio; Mistral/Llama-class
|
|
4527
|
+
// models) — free tier of the pricing pivot; nothing leaves the user's machine.
|
|
4528
|
+
local: localModelRunner,
|
|
4322
4529
|
meta: metaRunner,
|
|
4323
4530
|
oai: openaiCompatibleRunner
|
|
4324
4531
|
};
|
|
@@ -5983,6 +6190,12 @@ function makeLoopTicks({
|
|
|
5983
6190
|
applyCapacity: () => false,
|
|
5984
6191
|
heartbeatFields: () => ({})
|
|
5985
6192
|
},
|
|
6193
|
+
// Track 1: served-local-model reporting + desired-model echo application
|
|
6194
|
+
// (local-model-remote-config.mjs). No-op defaults keep old callers working.
|
|
6195
|
+
localModelController = {
|
|
6196
|
+
applyRemoteConfig: () => false,
|
|
6197
|
+
heartbeatFields: () => ({})
|
|
6198
|
+
},
|
|
5986
6199
|
// Cached agent-availability provider (agent-availability.mjs); returns null
|
|
5987
6200
|
// until the first probe completes — the heartbeat simply omits the field.
|
|
5988
6201
|
getAgentAvailability = () => null,
|
|
@@ -6020,7 +6233,10 @@ function makeLoopTicks({
|
|
|
6020
6233
|
} catch (error) {
|
|
6021
6234
|
request = Promise.reject(error);
|
|
6022
6235
|
}
|
|
6023
|
-
request.then((response) =>
|
|
6236
|
+
request.then((response) => {
|
|
6237
|
+
capacityController.applyCapacity(response?.capacity, nextPayload.operatorId);
|
|
6238
|
+
localModelController.applyRemoteConfig(response?.local_model, nextPayload.operatorId);
|
|
6239
|
+
}).catch((e) => log3(`heartbeat failed: ${e.message}`)).finally(() => {
|
|
6024
6240
|
for (const done of waiters) done();
|
|
6025
6241
|
if (state.pending) {
|
|
6026
6242
|
const pending = state.pending;
|
|
@@ -6062,6 +6278,7 @@ function makeLoopTicks({
|
|
|
6062
6278
|
const supervisorVersion = String(env2.VO_RUNNER_SUPERVISOR_VERSION || "").trim().slice(0, 40);
|
|
6063
6279
|
const supervisorCapabilities = String(env2.VO_RUNNER_SUPERVISOR_CAPABILITIES || "").split(",").map((value) => value.trim()).filter(Boolean).slice(0, 8);
|
|
6064
6280
|
const capacityFields = capacityController.heartbeatFields();
|
|
6281
|
+
const localModelFields = localModelController.heartbeatFields();
|
|
6065
6282
|
const baseHeartbeat = {
|
|
6066
6283
|
runnerId: cfg.runnerId,
|
|
6067
6284
|
...runnerInstanceId ? { runnerInstanceId } : {},
|
|
@@ -6078,7 +6295,8 @@ function makeLoopTicks({
|
|
|
6078
6295
|
uptimeSec: Math.floor(process.uptime()),
|
|
6079
6296
|
activeTasks: getActive(),
|
|
6080
6297
|
maxConcurrency: cfg.maxConcurrency,
|
|
6081
|
-
...capacityFields
|
|
6298
|
+
...capacityFields,
|
|
6299
|
+
...localModelFields
|
|
6082
6300
|
};
|
|
6083
6301
|
const operatorIds = servedOperators.length > 0 ? servedOperators : [void 0];
|
|
6084
6302
|
for (const operatorId of operatorIds) {
|
|
@@ -6245,6 +6463,127 @@ var init_agent_availability = __esm({
|
|
|
6245
6463
|
}
|
|
6246
6464
|
});
|
|
6247
6465
|
|
|
6466
|
+
// ../../scripts/virtual-office/code-runner/local-model-remote-config.mjs
|
|
6467
|
+
async function listServedLocalModels({
|
|
6468
|
+
env: env2 = process.env,
|
|
6469
|
+
fetchImpl = globalThis.fetch,
|
|
6470
|
+
timeoutMs = 1500
|
|
6471
|
+
} = {}) {
|
|
6472
|
+
const provider = resolveLocalProvider(env2);
|
|
6473
|
+
if (!LOCAL_PROVIDERS.includes(provider)) return null;
|
|
6474
|
+
const override = resolveLocalBaseUrl(env2);
|
|
6475
|
+
if (override && !isLoopbackBaseUrl(override)) return null;
|
|
6476
|
+
const url = provider === "ollama" && override ? `${override.replace(/\/+$/, "")}/api/tags` : SERVED_MODELS_PROBE_URLS[provider];
|
|
6477
|
+
try {
|
|
6478
|
+
const res = await fetchImpl(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
6479
|
+
if (!res?.ok) return null;
|
|
6480
|
+
const json = await res.json();
|
|
6481
|
+
const names = provider === "ollama" ? Array.isArray(json?.models) ? json.models.map((m) => m?.name) : null : Array.isArray(json?.data) ? json.data.map((m) => m?.id) : null;
|
|
6482
|
+
if (!names) return null;
|
|
6483
|
+
return names.filter((name) => typeof name === "string" && isValidLocalModel(name)).slice(0, MAX_REPORTED_MODELS);
|
|
6484
|
+
} catch {
|
|
6485
|
+
return null;
|
|
6486
|
+
}
|
|
6487
|
+
}
|
|
6488
|
+
function createLocalModelRemoteController({
|
|
6489
|
+
env: env2 = process.env,
|
|
6490
|
+
fetchImpl = globalThis.fetch,
|
|
6491
|
+
log: log3 = () => {
|
|
6492
|
+
},
|
|
6493
|
+
probeIntervalMs = 6e4,
|
|
6494
|
+
now = () => Date.now(),
|
|
6495
|
+
apply = setRemoteDesiredLocalModel,
|
|
6496
|
+
listModels = listServedLocalModels
|
|
6497
|
+
} = {}) {
|
|
6498
|
+
let served = null;
|
|
6499
|
+
const byOperator = /* @__PURE__ */ new Map();
|
|
6500
|
+
let probing = false;
|
|
6501
|
+
let lastProbeAt = 0;
|
|
6502
|
+
let warnedUnserved = "";
|
|
6503
|
+
let warnedConflict = "";
|
|
6504
|
+
function desiredModel() {
|
|
6505
|
+
const models = [...new Set(
|
|
6506
|
+
[...byOperator.values()].map((s) => s.desired).filter((m) => typeof m === "string" && m)
|
|
6507
|
+
)];
|
|
6508
|
+
if (models.length > 1) {
|
|
6509
|
+
const key = models.slice().sort().join(",");
|
|
6510
|
+
if (warnedConflict !== key) {
|
|
6511
|
+
warnedConflict = key;
|
|
6512
|
+
log3(`local-model remote config: conflicting desired models across served operators (${key}) \u2014 applying none`);
|
|
6513
|
+
}
|
|
6514
|
+
return null;
|
|
6515
|
+
}
|
|
6516
|
+
return models[0] ?? null;
|
|
6517
|
+
}
|
|
6518
|
+
function syncEffective() {
|
|
6519
|
+
const desired = desiredModel();
|
|
6520
|
+
const effective = desired && Array.isArray(served) && served.includes(desired) ? desired : "";
|
|
6521
|
+
apply(effective);
|
|
6522
|
+
if (desired && !effective && warnedUnserved !== desired) {
|
|
6523
|
+
warnedUnserved = desired;
|
|
6524
|
+
log3(
|
|
6525
|
+
`local-model remote config: "${desired}" is not served by the local inference server \u2014 ignored (never auto-pull; pull it locally first)`
|
|
6526
|
+
);
|
|
6527
|
+
}
|
|
6528
|
+
return effective;
|
|
6529
|
+
}
|
|
6530
|
+
function refreshServedModels() {
|
|
6531
|
+
if (probing || now() - lastProbeAt < probeIntervalMs) return;
|
|
6532
|
+
probing = true;
|
|
6533
|
+
lastProbeAt = now();
|
|
6534
|
+
Promise.resolve(listModels({ env: env2, fetchImpl })).then((models) => {
|
|
6535
|
+
if (Array.isArray(models)) served = models;
|
|
6536
|
+
syncEffective();
|
|
6537
|
+
}).catch(() => {
|
|
6538
|
+
}).finally(() => {
|
|
6539
|
+
probing = false;
|
|
6540
|
+
});
|
|
6541
|
+
}
|
|
6542
|
+
return {
|
|
6543
|
+
/** Heartbeat payload extras; also kicks the throttled background probe. */
|
|
6544
|
+
heartbeatFields() {
|
|
6545
|
+
refreshServedModels();
|
|
6546
|
+
return Array.isArray(served) && served.length > 0 ? { availableLocalModels: served } : {};
|
|
6547
|
+
},
|
|
6548
|
+
/**
|
|
6549
|
+
* Apply one operator's heartbeat-response echo
|
|
6550
|
+
* { schema_version, desired_local_model, revision }. Revision ordering is
|
|
6551
|
+
* PER OPERATOR — counters are independent across operator scopes.
|
|
6552
|
+
*/
|
|
6553
|
+
applyRemoteConfig(echo, operatorId = "") {
|
|
6554
|
+
const scope = String(operatorId || "");
|
|
6555
|
+
const previous = byOperator.get(scope);
|
|
6556
|
+
if (!echo || echo.schema_version !== 1 || !Number.isInteger(echo.revision) || previous && echo.revision < previous.revision) {
|
|
6557
|
+
return false;
|
|
6558
|
+
}
|
|
6559
|
+
const model = echo.desired_local_model;
|
|
6560
|
+
if (model !== null && (typeof model !== "string" || !isValidLocalModel(model))) {
|
|
6561
|
+
return false;
|
|
6562
|
+
}
|
|
6563
|
+
byOperator.set(scope, { revision: echo.revision, desired: model });
|
|
6564
|
+
syncEffective();
|
|
6565
|
+
return true;
|
|
6566
|
+
},
|
|
6567
|
+
snapshot: () => ({
|
|
6568
|
+
served,
|
|
6569
|
+
desired: desiredModel(),
|
|
6570
|
+
operators: Object.fromEntries(byOperator)
|
|
6571
|
+
})
|
|
6572
|
+
};
|
|
6573
|
+
}
|
|
6574
|
+
var SERVED_MODELS_PROBE_URLS, MAX_REPORTED_MODELS;
|
|
6575
|
+
var init_local_model_remote_config = __esm({
|
|
6576
|
+
"../../scripts/virtual-office/code-runner/local-model-remote-config.mjs"() {
|
|
6577
|
+
"use strict";
|
|
6578
|
+
init_local_model_runner();
|
|
6579
|
+
SERVED_MODELS_PROBE_URLS = {
|
|
6580
|
+
ollama: "http://127.0.0.1:11434/api/tags",
|
|
6581
|
+
lmstudio: "http://127.0.0.1:1234/v1/models"
|
|
6582
|
+
};
|
|
6583
|
+
MAX_REPORTED_MODELS = 50;
|
|
6584
|
+
}
|
|
6585
|
+
});
|
|
6586
|
+
|
|
6248
6587
|
// ../../scripts/virtual-office/code-runner/account-usage.mjs
|
|
6249
6588
|
import { spawn as spawn4 } from "node:child_process";
|
|
6250
6589
|
import fs6 from "node:fs";
|
|
@@ -6854,7 +7193,8 @@ function makeWatchRunner({
|
|
|
6854
7193
|
maxFixAttempts,
|
|
6855
7194
|
repairEvidence = readCiRepairEvidence,
|
|
6856
7195
|
stateFile = DEFAULT_STATE_FILE,
|
|
6857
|
-
|
|
7196
|
+
// Default ON (operator directive 2026-07-24); VO_CODE_RUNNER_ARM_AUTOMERGE=0 opts out.
|
|
7197
|
+
autoMergeEnabled = process.env.VO_CODE_RUNNER_ARM_AUTOMERGE !== "0"
|
|
6858
7198
|
}) {
|
|
6859
7199
|
return () => runWatchCycle({
|
|
6860
7200
|
viewPr,
|
|
@@ -7511,7 +7851,7 @@ var init_model_router = __esm({
|
|
|
7511
7851
|
"use strict";
|
|
7512
7852
|
init_model_registry();
|
|
7513
7853
|
init_meta_model_catalog();
|
|
7514
|
-
TASK_MODEL_AGENTS = ["claude", "codex", "cursor", "meta"];
|
|
7854
|
+
TASK_MODEL_AGENTS = ["claude", "codex", "cursor", "local", "meta"];
|
|
7515
7855
|
DEFAULT_AGENT2 = "claude";
|
|
7516
7856
|
AGENT_TIER_FAMILIES = {
|
|
7517
7857
|
claude: {
|
|
@@ -7533,6 +7873,13 @@ var init_model_router = __esm({
|
|
|
7533
7873
|
mid: null,
|
|
7534
7874
|
best: null
|
|
7535
7875
|
},
|
|
7876
|
+
// Local models are machine-specific pulls (Ollama / LM Studio); the runner's
|
|
7877
|
+
// VO_CODE_RUNNER_LOCAL_MODEL is the source of truth, not the registry.
|
|
7878
|
+
local: {
|
|
7879
|
+
cheap: null,
|
|
7880
|
+
mid: null,
|
|
7881
|
+
best: null
|
|
7882
|
+
},
|
|
7536
7883
|
meta: {
|
|
7537
7884
|
cheap: null,
|
|
7538
7885
|
mid: null,
|
|
@@ -7555,6 +7902,11 @@ var init_model_router = __esm({
|
|
|
7555
7902
|
mid: null,
|
|
7556
7903
|
best: null
|
|
7557
7904
|
},
|
|
7905
|
+
local: {
|
|
7906
|
+
cheap: null,
|
|
7907
|
+
mid: null,
|
|
7908
|
+
best: null
|
|
7909
|
+
},
|
|
7558
7910
|
meta: {
|
|
7559
7911
|
cheap: resolveMetaModelForTier("cheap"),
|
|
7560
7912
|
mid: resolveMetaModelForTier("mid"),
|
|
@@ -7573,6 +7925,16 @@ var init_model_router = __esm({
|
|
|
7573
7925
|
// cursor-runner. Restrict to the shape a model id actually has so a payload
|
|
7574
7926
|
// like `x & powershell -enc ...` is rejected before it reaches a spawn.
|
|
7575
7927
|
cursor: (model) => /^[A-Za-z0-9][A-Za-z0-9._:@\[\]-]{0,79}$/.test(String(model || "")),
|
|
7928
|
+
// SECURITY: the local lane accepts NO remote model pins — return false for
|
|
7929
|
+
// EVERY value so control-plane `task.model` can never influence what runs on
|
|
7930
|
+
// the user's machine. codex `--oss` AUTO-PULLS missing models (live-verified
|
|
7931
|
+
// 2026-07-24: a single --model argument downloaded 397MB unprompted), so an
|
|
7932
|
+
// honored pin like "llama3.1:405b" (231GB, valid id shape) would let any
|
|
7933
|
+
// tenant member remotely fill a BYO runner owner's disk. The machine-local
|
|
7934
|
+
// env (VO_CODE_RUNNER_LOCAL_MODEL — the owner's own choice in the app) is
|
|
7935
|
+
// the ONLY model authority, mirroring the runner-release-target principle
|
|
7936
|
+
// that browser input is never local-execution authority.
|
|
7937
|
+
local: () => false,
|
|
7576
7938
|
meta: (model) => /^muse-spark-[A-Za-z0-9._:@\[\]-]{0,79}$/i.test(String(model || ""))
|
|
7577
7939
|
};
|
|
7578
7940
|
}
|
|
@@ -8569,6 +8931,198 @@ var init_agent_process_env = __esm({
|
|
|
8569
8931
|
}
|
|
8570
8932
|
});
|
|
8571
8933
|
|
|
8934
|
+
// ../../scripts/virtual-office/code-runner/inference-executor.mjs
|
|
8935
|
+
function buildChatBody({ messages, prompt, model, maxTokens, temperature } = {}) {
|
|
8936
|
+
const msgs = Array.isArray(messages) && messages.length > 0 ? messages : [{ role: "user", content: String(prompt ?? "") }];
|
|
8937
|
+
if (msgs.length > MAX_INFERENCE_MESSAGES) {
|
|
8938
|
+
throw new Error(`inference: too many messages (${msgs.length} > ${MAX_INFERENCE_MESSAGES})`);
|
|
8939
|
+
}
|
|
8940
|
+
const totalChars = msgs.reduce((n, m) => n + String(m?.content ?? "").length, 0);
|
|
8941
|
+
if (totalChars > MAX_INFERENCE_PROMPT_CHARS) {
|
|
8942
|
+
throw new Error(`inference: prompt too large (${totalChars} > ${MAX_INFERENCE_PROMPT_CHARS} chars)`);
|
|
8943
|
+
}
|
|
8944
|
+
if (!model || typeof model !== "string") {
|
|
8945
|
+
throw new Error("inference: a model id is required");
|
|
8946
|
+
}
|
|
8947
|
+
const body = {
|
|
8948
|
+
model,
|
|
8949
|
+
messages: msgs.map((m) => ({ role: String(m.role || "user"), content: String(m.content ?? "") })),
|
|
8950
|
+
stream: false,
|
|
8951
|
+
max_tokens: Number.isInteger(maxTokens) && maxTokens > 0 ? maxTokens : DEFAULT_MAX_OUTPUT_TOKENS
|
|
8952
|
+
};
|
|
8953
|
+
if (Number.isFinite(temperature)) body.temperature = temperature;
|
|
8954
|
+
return body;
|
|
8955
|
+
}
|
|
8956
|
+
function resolveInferenceEndpoint({ providerMode, baseUrl } = {}) {
|
|
8957
|
+
const mode = String(providerMode || "local").toLowerCase();
|
|
8958
|
+
if (mode !== "local") {
|
|
8959
|
+
throw new Error(`inference: unsupported provider mode "${mode}" (this slice is local-only)`);
|
|
8960
|
+
}
|
|
8961
|
+
const url = String(baseUrl || DEFAULT_LOCAL_INFERENCE_BASE_URL).replace(/\/+$/, "");
|
|
8962
|
+
if (!isLoopbackBaseUrl(url)) {
|
|
8963
|
+
throw new Error(`inference: local base_url must be loopback (localhost/127.0.0.1/[::1]); got "${url}"`);
|
|
8964
|
+
}
|
|
8965
|
+
return url;
|
|
8966
|
+
}
|
|
8967
|
+
function extractCompletion(json) {
|
|
8968
|
+
const choice = json?.choices?.[0];
|
|
8969
|
+
const text = choice?.message?.content ?? choice?.text ?? "";
|
|
8970
|
+
if (typeof text !== "string" || text.length === 0) {
|
|
8971
|
+
throw new Error("inference: provider returned no completion text");
|
|
8972
|
+
}
|
|
8973
|
+
const usage = json?.usage ?? null;
|
|
8974
|
+
return {
|
|
8975
|
+
text,
|
|
8976
|
+
usage: usage ? {
|
|
8977
|
+
input_tokens: Number(usage.prompt_tokens) || 0,
|
|
8978
|
+
output_tokens: Number(usage.completion_tokens) || 0,
|
|
8979
|
+
total_tokens: Number(usage.total_tokens) || 0
|
|
8980
|
+
} : null,
|
|
8981
|
+
finish_reason: choice?.finish_reason ?? null
|
|
8982
|
+
};
|
|
8983
|
+
}
|
|
8984
|
+
async function runInference({ providerMode = "local", baseUrl, model, prompt, messages, maxTokens, temperature, timeoutMs = 12e4 } = {}, { fetchImpl = globalThis.fetch } = {}) {
|
|
8985
|
+
const endpoint = resolveInferenceEndpoint({ providerMode, baseUrl });
|
|
8986
|
+
const body = buildChatBody({ messages, prompt, model, maxTokens, temperature });
|
|
8987
|
+
const res = await fetchImpl(`${endpoint}/chat/completions`, {
|
|
8988
|
+
method: "POST",
|
|
8989
|
+
headers: { "Content-Type": "application/json" },
|
|
8990
|
+
body: JSON.stringify(body),
|
|
8991
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
8992
|
+
});
|
|
8993
|
+
if (!res?.ok) {
|
|
8994
|
+
const status = res?.status ?? "ERR";
|
|
8995
|
+
let detail = "";
|
|
8996
|
+
try {
|
|
8997
|
+
detail = (await res.text()).slice(0, 300);
|
|
8998
|
+
} catch {
|
|
8999
|
+
}
|
|
9000
|
+
throw new Error(`inference: endpoint returned HTTP ${status}${detail ? ` \u2014 ${detail}` : ""}`);
|
|
9001
|
+
}
|
|
9002
|
+
const json = await res.json();
|
|
9003
|
+
const { text, usage, finish_reason } = extractCompletion(json);
|
|
9004
|
+
return { text, usage, finish_reason, model, endpoint };
|
|
9005
|
+
}
|
|
9006
|
+
var DEFAULT_LOCAL_INFERENCE_BASE_URL, MAX_INFERENCE_MESSAGES, MAX_INFERENCE_PROMPT_CHARS, DEFAULT_MAX_OUTPUT_TOKENS;
|
|
9007
|
+
var init_inference_executor = __esm({
|
|
9008
|
+
"../../scripts/virtual-office/code-runner/inference-executor.mjs"() {
|
|
9009
|
+
"use strict";
|
|
9010
|
+
init_local_model_runner();
|
|
9011
|
+
DEFAULT_LOCAL_INFERENCE_BASE_URL = "http://127.0.0.1:11434/v1";
|
|
9012
|
+
MAX_INFERENCE_MESSAGES = 200;
|
|
9013
|
+
MAX_INFERENCE_PROMPT_CHARS = 1e5;
|
|
9014
|
+
DEFAULT_MAX_OUTPUT_TOKENS = 1024;
|
|
9015
|
+
}
|
|
9016
|
+
});
|
|
9017
|
+
|
|
9018
|
+
// ../../scripts/virtual-office/code-runner/inference-task-handler.mjs
|
|
9019
|
+
async function handleInferenceTask(task, { runInferenceImpl = runInference, now = () => Date.now() } = {}) {
|
|
9020
|
+
const started = now();
|
|
9021
|
+
try {
|
|
9022
|
+
const result = await runInferenceImpl({
|
|
9023
|
+
providerMode: "local",
|
|
9024
|
+
baseUrl: task.base_url,
|
|
9025
|
+
model: task.model,
|
|
9026
|
+
prompt: task.prompt,
|
|
9027
|
+
messages: task.messages,
|
|
9028
|
+
maxTokens: task.max_output_tokens,
|
|
9029
|
+
temperature: task.temperature
|
|
9030
|
+
});
|
|
9031
|
+
const text = String(result.text || "").slice(0, MAX_RESULT_CHARS);
|
|
9032
|
+
return {
|
|
9033
|
+
ok: true,
|
|
9034
|
+
text,
|
|
9035
|
+
usage: result.usage ?? null,
|
|
9036
|
+
finish_reason: result.finish_reason ?? null,
|
|
9037
|
+
provider_mode: "local",
|
|
9038
|
+
// Structural $0: no AlgoSuite provider call happened, so nothing to charge.
|
|
9039
|
+
billable: false,
|
|
9040
|
+
duration_ms: now() - started
|
|
9041
|
+
};
|
|
9042
|
+
} catch (err) {
|
|
9043
|
+
return {
|
|
9044
|
+
ok: false,
|
|
9045
|
+
provider_mode: "local",
|
|
9046
|
+
error: err instanceof Error ? err.message : String(err),
|
|
9047
|
+
duration_ms: now() - started
|
|
9048
|
+
};
|
|
9049
|
+
}
|
|
9050
|
+
}
|
|
9051
|
+
var MAX_RESULT_CHARS;
|
|
9052
|
+
var init_inference_task_handler = __esm({
|
|
9053
|
+
"../../scripts/virtual-office/code-runner/inference-task-handler.mjs"() {
|
|
9054
|
+
"use strict";
|
|
9055
|
+
init_inference_executor();
|
|
9056
|
+
MAX_RESULT_CHARS = 1e5;
|
|
9057
|
+
}
|
|
9058
|
+
});
|
|
9059
|
+
|
|
9060
|
+
// ../../scripts/virtual-office/code-runner/inference-task-runner.mjs
|
|
9061
|
+
async function processInferenceTask(client, task, cfg, {
|
|
9062
|
+
env: env2 = process.env,
|
|
9063
|
+
safeProgress: safeProgress2,
|
|
9064
|
+
runnerStagePatch: runnerStagePatch2,
|
|
9065
|
+
handle = handleInferenceTask,
|
|
9066
|
+
log: log3 = () => {
|
|
9067
|
+
}
|
|
9068
|
+
} = {}) {
|
|
9069
|
+
const id = task.code_task_id;
|
|
9070
|
+
await safeProgress2(
|
|
9071
|
+
client,
|
|
9072
|
+
id,
|
|
9073
|
+
runnerStagePatch2("starting_agent", `${cfg.runnerId} running local inference for this task`, {
|
|
9074
|
+
status: "running"
|
|
9075
|
+
})
|
|
9076
|
+
);
|
|
9077
|
+
const model = resolveLocalModel(env2);
|
|
9078
|
+
if (!model) {
|
|
9079
|
+
await safeProgress2(client, id, {
|
|
9080
|
+
status: "failed",
|
|
9081
|
+
message: "inference: this runner has no local model configured",
|
|
9082
|
+
result: "This computer is not configured with a local model. Set VO_CODE_RUNNER_LOCAL_MODEL (a model your Ollama / LM Studio already has), then retry."
|
|
9083
|
+
});
|
|
9084
|
+
return;
|
|
9085
|
+
}
|
|
9086
|
+
const out = await handle(
|
|
9087
|
+
{
|
|
9088
|
+
provider_mode: "local",
|
|
9089
|
+
model,
|
|
9090
|
+
prompt: task.prompt,
|
|
9091
|
+
base_url: resolveLocalBaseUrl(env2) || void 0
|
|
9092
|
+
},
|
|
9093
|
+
{ now: () => Date.now() }
|
|
9094
|
+
);
|
|
9095
|
+
if (out.ok) {
|
|
9096
|
+
log3(`inference task ${id} completed on ${model} (${out.usage?.output_tokens ?? "?"} output tokens)`);
|
|
9097
|
+
await safeProgress2(client, id, {
|
|
9098
|
+
status: INFERENCE_SUCCESS_STATUS,
|
|
9099
|
+
message: "inference complete",
|
|
9100
|
+
result: out.text,
|
|
9101
|
+
// $0 by construction — the completion was produced on the user's machine,
|
|
9102
|
+
// AlgoSuite made no provider call, so there is nothing to bill.
|
|
9103
|
+
cost_usd: 0,
|
|
9104
|
+
num_turns: 1
|
|
9105
|
+
});
|
|
9106
|
+
return;
|
|
9107
|
+
}
|
|
9108
|
+
log3(`inference task ${id} failed: ${out.error}`);
|
|
9109
|
+
await safeProgress2(client, id, {
|
|
9110
|
+
status: "failed",
|
|
9111
|
+
message: "inference failed",
|
|
9112
|
+
result: out.error,
|
|
9113
|
+
cost_usd: 0
|
|
9114
|
+
});
|
|
9115
|
+
}
|
|
9116
|
+
var INFERENCE_SUCCESS_STATUS;
|
|
9117
|
+
var init_inference_task_runner = __esm({
|
|
9118
|
+
"../../scripts/virtual-office/code-runner/inference-task-runner.mjs"() {
|
|
9119
|
+
"use strict";
|
|
9120
|
+
init_local_model_runner();
|
|
9121
|
+
init_inference_task_handler();
|
|
9122
|
+
INFERENCE_SUCCESS_STATUS = "no_changes_needed";
|
|
9123
|
+
}
|
|
9124
|
+
});
|
|
9125
|
+
|
|
8572
9126
|
// ../../scripts/virtual-office/code-runner/isolation-audit.mjs
|
|
8573
9127
|
import fs8 from "node:fs";
|
|
8574
9128
|
import fsp9 from "node:fs/promises";
|
|
@@ -9001,7 +9555,8 @@ function loadConfig(env2 = process.env) {
|
|
|
9001
9555
|
watchEnabled: env2.VO_CODE_RUNNER_WATCH !== "0",
|
|
9002
9556
|
watchMaxFix: Math.max(0, Number(env2.VO_CODE_RUNNER_WATCH_MAX_FIX ?? 1) || 0),
|
|
9003
9557
|
watchIntervalSec: Math.max(30, Number(env2.VO_CODE_RUNNER_WATCH_SEC ?? 60) || 60),
|
|
9004
|
-
armAutoMerge: env2.VO_CODE_RUNNER_ARM_AUTOMERGE
|
|
9558
|
+
armAutoMerge: env2.VO_CODE_RUNNER_ARM_AUTOMERGE !== "0",
|
|
9559
|
+
// default ON (operator directive 2026-07-24); =0 opts out; receipt-gated consensus merge stays the mechanism
|
|
9005
9560
|
// In-product runner control. Off: VO_CODE_RUNNER_CONTROL=0.
|
|
9006
9561
|
controlEnabled: env2.VO_CODE_RUNNER_CONTROL !== "0",
|
|
9007
9562
|
controlPort: Math.max(1, Number(env2.VO_CODE_RUNNER_CONTROL_PORT ?? 7787) || 7787),
|
|
@@ -9231,7 +9786,7 @@ async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
|
9231
9786
|
);
|
|
9232
9787
|
for (const line of describeClaimScoping(cfg, env2)) log2(line);
|
|
9233
9788
|
log2(
|
|
9234
|
-
cfg.armAutoMerge ? "PR auto-merge arming ON for complete PRs (VO_CODE_RUNNER_ARM_AUTOMERGE=
|
|
9789
|
+
cfg.armAutoMerge ? "PR auto-merge arming ON for complete PRs (default; VO_CODE_RUNNER_ARM_AUTOMERGE=0 to disable)" : "PR auto-merge arming OFF (VO_CODE_RUNNER_ARM_AUTOMERGE=0)"
|
|
9235
9790
|
);
|
|
9236
9791
|
log2(
|
|
9237
9792
|
watchCyclesEnabled ? `PR watcher ON \u2014 auto-fix ${cfg.watchMaxFix}/PR; ${cfg.armAutoMerge ? "receipt-gated merge enabled" : "merge disabled"}; every ${cfg.watchIntervalSec}s (VO_CODE_RUNNER_WATCH=0 to disable)` : once2 ? "PR watcher bypassed for --once; dispatched PRs remain tracked" : "PR watcher OFF (VO_CODE_RUNNER_WATCH=0)"
|
|
@@ -9240,7 +9795,7 @@ async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
|
9240
9795
|
const watchCoordinator = makeWatchCycleCoordinator({ runWatch, log: log2, intervalMs: cfg.watchIntervalSec * 1e3 });
|
|
9241
9796
|
const agentAvailability = makeAgentAvailabilityProvider({ onError: (e) => log2(`agent probe failed: ${e.message}`) });
|
|
9242
9797
|
const accountUsage = makeAccountUsageProvider();
|
|
9243
|
-
const loopTick = makeLoopTicks({ client, cfg, env: env2, log: log2, getActive: () => active, runnerInstanceId, capacityController, getAgentAvailability: () => agentAvailability.get(), getAccountUsage: () => accountUsage.get() });
|
|
9798
|
+
const loopTick = makeLoopTicks({ client, cfg, env: env2, log: log2, getActive: () => active, runnerInstanceId, capacityController, localModelController: createLocalModelRemoteController({ env: env2, log: log2 }), getAgentAvailability: () => agentAvailability.get(), getAccountUsage: () => accountUsage.get() });
|
|
9244
9799
|
const backoff = makeReconnectBackoff({ baseMs: cfg.pollSec * 1e3, log: log2 });
|
|
9245
9800
|
while (!stopping) {
|
|
9246
9801
|
const heartbeatCompletion = loopTick();
|
|
@@ -9283,7 +9838,8 @@ async function main({ env: env2 = process.env, once: once2 = false } = {}) {
|
|
|
9283
9838
|
}
|
|
9284
9839
|
log2(`claimed task ${task.code_task_id} (${task.repo})`);
|
|
9285
9840
|
active += 1;
|
|
9286
|
-
const
|
|
9841
|
+
const runTask = task.kind === "inference" ? processInferenceTask(client, task, cfg, { safeProgress, runnerStagePatch, log: log2 }) : processOneTask(client, task, cfg);
|
|
9842
|
+
const done = runTask.finally(() => {
|
|
9287
9843
|
active -= 1;
|
|
9288
9844
|
});
|
|
9289
9845
|
if (once2) {
|
|
@@ -9316,6 +9872,7 @@ var init_code_runner_daemon = __esm({
|
|
|
9316
9872
|
init_loop_ticks();
|
|
9317
9873
|
init_runner_capacity();
|
|
9318
9874
|
init_agent_availability();
|
|
9875
|
+
init_local_model_remote_config();
|
|
9319
9876
|
init_account_usage();
|
|
9320
9877
|
init_pr_watcher();
|
|
9321
9878
|
init_existing_pr_target();
|
|
@@ -9326,6 +9883,7 @@ var init_code_runner_daemon = __esm({
|
|
|
9326
9883
|
init_reconnect_backoff();
|
|
9327
9884
|
init_task_helpers();
|
|
9328
9885
|
init_agent_process_env();
|
|
9886
|
+
init_inference_task_runner();
|
|
9329
9887
|
init_isolation_audit();
|
|
9330
9888
|
init_recovery_ledger();
|
|
9331
9889
|
init_no_changes_terminal_status();
|