@mcoda/mswarm 0.1.96 → 0.1.98

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/runtime.js CHANGED
@@ -4274,6 +4274,65 @@ export class SelfHostedNodeRuntime {
4274
4274
  await writeSelfHostedRuntimeToken(this.config.runtimeTokenPath, runtimeToken);
4275
4275
  return { runtimeToken, state: nextState, enrolled: true };
4276
4276
  }
4277
+ /**
4278
+ * Forwards an OpenAI-standard tool-calling request straight to the agent's
4279
+ * local runner and returns its raw response, tool_calls included.
4280
+ *
4281
+ * The Codali runtime deliberately owns tool execution and never surfaces raw
4282
+ * tool_calls to the caller, which is correct when Codali is orchestrating. A
4283
+ * client that supplies its own `tools` is orchestrating instead, so it needs
4284
+ * the model's unexecuted tool calls back.
4285
+ */
4286
+ async executeOpenAiToolPassthrough(job, agent) {
4287
+ const baseUrl = optionalText(agent.baseUrl);
4288
+ if (!baseUrl) {
4289
+ throw new SelfHostedPreStartJobError("selected_agent_unavailable", "agent has no local runner base URL for OpenAI tool pass-through");
4290
+ }
4291
+ const headers = { "content-type": "application/json" };
4292
+ const bearer = agent.authMode === "bearer"
4293
+ ? optionalText(agent.apiKey)
4294
+ : agent.authMode === "dummy-bearer"
4295
+ ? optionalText(agent.dummyBearerToken) || "local"
4296
+ : undefined;
4297
+ if (bearer) {
4298
+ headers.authorization = `Bearer ${bearer}`;
4299
+ }
4300
+ for (const [key, value] of Object.entries(agent.headers ?? {})) {
4301
+ if (typeof value === "string") {
4302
+ headers[key.toLowerCase()] = value;
4303
+ }
4304
+ }
4305
+ const body = {
4306
+ model: optionalText(agent.model) || job.openai_request.model,
4307
+ messages: job.openai_request.messages,
4308
+ tools: job.openai_request.tools,
4309
+ stream: false
4310
+ };
4311
+ if (job.openai_request.tool_choice !== undefined)
4312
+ body.tool_choice = job.openai_request.tool_choice;
4313
+ if (job.openai_request.temperature !== undefined)
4314
+ body.temperature = job.openai_request.temperature;
4315
+ if (job.openai_request.top_p !== undefined)
4316
+ body.top_p = job.openai_request.top_p;
4317
+ if (job.openai_request.max_tokens !== undefined)
4318
+ body.max_tokens = job.openai_request.max_tokens;
4319
+ if (job.openai_request.stop !== undefined)
4320
+ body.stop = job.openai_request.stop;
4321
+ const url = `${baseUrl.replace(/\/+$/, "")}/chat/completions`;
4322
+ const response = await fetch(url, { method: "POST", headers, body: JSON.stringify(body) });
4323
+ const text = await response.text();
4324
+ let payload = null;
4325
+ try {
4326
+ payload = text ? JSON.parse(text) : null;
4327
+ }
4328
+ catch {
4329
+ payload = null;
4330
+ }
4331
+ if (!response.ok || !payload || typeof payload !== "object") {
4332
+ throw new Error(`local runner OpenAI tool pass-through failed with HTTP ${response.status}: ${text.slice(0, 300)}`);
4333
+ }
4334
+ return payload;
4335
+ }
4277
4336
  async resolveMcodaAgentForJob(job) {
4278
4337
  const selectedSourceAgentSlug = optionalText(job.source_agent_slug);
4279
4338
  const selectedAgentSlug = optionalText(job.agent_slug);
@@ -4617,6 +4676,23 @@ export class SelfHostedNodeRuntime {
4617
4676
  adapter: agent.adapter,
4618
4677
  supports_tools: agent.supportsTools === true
4619
4678
  });
4679
+ // OpenAI-standard tool pass-through. When the caller supplies `tools` it is
4680
+ // driving its own tool loop, so the node must return the model's raw
4681
+ // tool_calls instead of running the Codali agentic loop, which owns tools
4682
+ // itself and would never surface them to the caller.
4683
+ if (Array.isArray(job.openai_request.tools) && job.openai_request.tools.length > 0) {
4684
+ const passthrough = await this.executeOpenAiToolPassthrough(job, agent);
4685
+ this.finishExecutionTelemetry({ executionClass: "llm", startedAt, ok: true });
4686
+ const passthroughResult = {
4687
+ job_id: job.job_id,
4688
+ request_id: job.request_id,
4689
+ status: "success",
4690
+ openai_response: passthrough,
4691
+ ...(progressEvents.length ? { progress_events: progressEvents } : {}),
4692
+ timing: { local_latency_ms: Date.now() - startedAt }
4693
+ };
4694
+ return passthroughResult;
4695
+ }
4620
4696
  const codaliGateway = buildCodaliGateway(job);
4621
4697
  const codaliJob = buildCodaliJob(job);
4622
4698
  const codaliSession = buildCodaliSession(job);