Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
f6b0955
Generated with Hive: Add resolveRequestUrl helper and persist request…
tomsmith8 Jun 22, 2026
10401da
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 22, 2026
91ed287
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 23, 2026
ef4a632
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 23, 2026
ca9a8ca
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 23, 2026
1efe0a7
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 23, 2026
80f07d7
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 24, 2026
000b248
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 24, 2026
430cd38
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 24, 2026
5f119f0
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 25, 2026
8f7602d
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 25, 2026
4be73ad
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 25, 2026
b8ddcdf
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 26, 2026
4f815fb
Generated with Hive: Retry cargo fetch in e2e workflow to fix Rust de…
tomsmith8 Jun 26, 2026
173ad74
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 26, 2026
54640dc
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 26, 2026
9d49d81
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 26, 2026
d5d9cd8
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 29, 2026
d30770c
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 30, 2026
06aa60a
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 30, 2026
495bbc5
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 30, 2026
4bd7ec5
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jun 30, 2026
1673310
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 1, 2026
51605ff
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 1, 2026
968c0de
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 1, 2026
eb297a7
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 2, 2026
d66e78b
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 2, 2026
5c47ce8
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 2, 2026
86a0644
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 3, 2026
d8314f2
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 4, 2026
6da2c9f
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 4, 2026
a44d12d
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 5, 2026
47821b4
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 6, 2026
d2f0cc8
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 6, 2026
270b20d
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 6, 2026
b159952
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 7, 2026
6833dcf
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 7, 2026
165c7a4
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 7, 2026
5afab20
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 8, 2026
a0dd49e
Generated with Hive: Fix LSP CI failures by installing TypeScript dep…
tomsmith8 Jul 8, 2026
2fabe83
Generated with Hive: Install TypeScript in nextjs test fixture to fix…
tomsmith8 Jul 8, 2026
4d3159a
Generated with Hive: Install npm dependencies for all TypeScript LSP …
tomsmith8 Jul 8, 2026
6069fc6
Generated with Hive: Fix npm dependency installation for TypeScript t…
tomsmith8 Jul 8, 2026
88205b0
Generated with Hive: Fix CI TypeScript installation path for LSP tests
tomsmith8 Jul 8, 2026
4440d9a
Merge remote-tracking branch 'origin/main' into feature/CMQP08748-add…
tomsmith8 Jul 8, 2026
8a0064c
Generated with Hive: Fix LSP CI by setting tsserver path and passing …
tomsmith8 Jul 8, 2026
c019c4f
Merge branch 'main' into feature/CMQP08748-add-resolve-request-url-to…
pitoi Jul 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/workflows/e2e-standalone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,22 @@ jobs:
done'
echo "Neo4j is healthy!"

- name: Fetch Rust dependencies (with retry)
run: |
for i in 1 2 3; do
cargo fetch && break
echo "cargo fetch attempt $i failed, retrying in 15s..."
sleep 15
done

- name: Build Rust Standalone Server
run: cargo build --bin standalone --features neo4j

- name: Start Rust Standalone Server
run: |
export TEST_REF_ID=1
export USE_LSP=0
cargo run --bin standalone --features neo4j > standalone.log 2>&1 &
./target/debug/standalone > standalone.log 2>&1 &
echo $! > standalone.pid

- name: Wait for Rust server
Expand Down
13 changes: 11 additions & 2 deletions .github/workflows/rust-test-lsp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ jobs:
- name: Install TypeScript and TypeScript LSP
run: |
npm install -g typescript typescript-language-server
export PATH="$(npm bin -g):$PATH"
echo "$(npm bin -g)" >> $GITHUB_PATH
NPM_GLOBAL_BIN="$(npm config get prefix)/bin"
NPM_GLOBAL_ROOT="$(npm root -g)"
echo "$NPM_GLOBAL_BIN" >> $GITHUB_PATH
echo "TSSERVER_PATH=$NPM_GLOBAL_ROOT/typescript/bin/tsserver" >> $GITHUB_ENV

- name: Verify TypeScript Installation
run: |
Expand Down Expand Up @@ -141,6 +143,13 @@ jobs:
which solargraph || echo "solargraph not found"
which ruby-lsp || echo "ruby-lsp not found"

# -------------------- Install TypeScript deps for test fixtures --------------------
- name: Install npm deps for TypeScript test fixtures
run: |
npm install --prefix ast/src/testing/nextjs
npm install --prefix ast/src/testing/react --legacy-peer-deps
npm install --prefix ast/src/testing/typescript

# -------------------- Rust full test with LSP --------------------
- name: Run rust library tests with LSP
run: USE_LSP=true cargo test -p ast --lib -- --test-threads=1
8 changes: 5 additions & 3 deletions lsp/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,15 @@ impl LspClient {
}
pub async fn init(&mut self) -> Result<InitializeResult> {
debug!("LSP init... {:?}", self.root);
let root_uri = Url::from_file_path(&self.root).map_err(|_| {
Error::validation(format!("Invalid root path for LSP: {:?}", self.root))
})?;
let ret = self
.server
.initialize(InitializeParams {
root_uri: Some(root_uri.clone()),
workspace_folders: Some(vec![WorkspaceFolder {
uri: Url::from_file_path(&self.root).map_err(|_| {
Error::validation(format!("Invalid root path for LSP: {:?}", self.root))
})?,
uri: root_uri,
name: "root".into(),
}]),
capabilities: ClientCapabilities {
Expand Down
11 changes: 10 additions & 1 deletion lsp/src/language.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,16 @@ impl Language {
match self {
Self::Rust => Vec::new(),
Self::Go => Vec::new(),
Self::Typescript => vec!["--stdio".to_string()],
Self::Typescript => {
let mut args = vec!["--stdio".to_string()];
if let Ok(tsserver_path) = std::env::var("TSSERVER_PATH") {
if !tsserver_path.is_empty() {
args.push("--tsserver-path".to_string());
args.push(tsserver_path);
}
}
args
}
Self::Python => Vec::new(),
Self::Ruby => Vec::new(),
Self::Kotlin => Vec::new(),
Expand Down
82 changes: 82 additions & 0 deletions mcp/src/aieo/src/__tests__/resolveRequestUrl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { test, expect } from "@playwright/test";
import { resolveRequestUrl } from "../provider.js";

// ---------------------------------------------------------------------------
// resolveRequestUrl — unit tests
//
// Verifies the fully-qualified HTTP endpoint returned per provider,
// both with an explicit baseUrl (gateway) and without (direct / no env).
// ---------------------------------------------------------------------------

test.describe("resolveRequestUrl — with explicit baseUrl (gateway)", () => {
test("anthropic → <gatewayBase>/messages", () => {
expect(resolveRequestUrl("anthropic", "http://gw:3000")).toBe(
"http://gw:3000/anthropic/v1/messages",
);
});

test("openai → <gatewayBase>/chat/completions", () => {
expect(resolveRequestUrl("openai", "http://gw:3000")).toBe(
"http://gw:3000/openai/v1/chat/completions",
);
});

test("openrouter → <gatewayBase>/chat/completions (rides openai path)", () => {
expect(resolveRequestUrl("openrouter", "http://gw:3000")).toBe(
"http://gw:3000/openai/v1/chat/completions",
);
});

test("google → <gatewayRoot>/openai/v1/chat/completions (compat path)", () => {
expect(resolveRequestUrl("google", "http://gw:3000")).toBe(
"http://gw:3000/openai/v1/chat/completions",
);
});

test("google strips provider suffix to recover gateway root", () => {
// gatewayUrlFor("google", "http://gw:3000") → "http://gw:3000/genai/v1beta"
// resolveRequestUrl must strip /genai/v1beta and re-target /openai/v1/chat/completions
const result = resolveRequestUrl("google", "http://gw:3000");
expect(result).toBe("http://gw:3000/openai/v1/chat/completions");
// Confirm the genai path is NOT present in the final URL
expect(result).not.toContain("/genai/");
});
});

test.describe("resolveRequestUrl — without baseUrl and no LLM_GATEWAY_URL env", () => {
// These tests assume LLM_GATEWAY_URL is not set in the test environment.
// If it is set, getGatewayBaseURL() will return a value and the test
// would produce a URL instead of undefined — skip gracefully in that case.

test("anthropic → undefined (no gateway configured)", () => {
if (process.env.LLM_GATEWAY_URL) {
test.skip(true, "LLM_GATEWAY_URL is set; skipping no-gateway test");
return;
}
expect(resolveRequestUrl("anthropic", undefined)).toBeUndefined();
});

test("openai → undefined (no gateway configured)", () => {
if (process.env.LLM_GATEWAY_URL) {
test.skip(true, "LLM_GATEWAY_URL is set; skipping no-gateway test");
return;
}
expect(resolveRequestUrl("openai", undefined)).toBeUndefined();
});

test("openrouter → undefined (no gateway configured)", () => {
if (process.env.LLM_GATEWAY_URL) {
test.skip(true, "LLM_GATEWAY_URL is set; skipping no-gateway test");
return;
}
expect(resolveRequestUrl("openrouter", undefined)).toBeUndefined();
});

test("google → undefined (no gateway configured)", () => {
if (process.env.LLM_GATEWAY_URL) {
test.skip(true, "LLM_GATEWAY_URL is set; skipping no-gateway test");
return;
}
expect(resolveRequestUrl("google", undefined)).toBeUndefined();
});
});
36 changes: 36 additions & 0 deletions mcp/src/aieo/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,42 @@ export function gatewayUrlFor(provider: Provider, baseUrl: string): string {
return `${trimmed}${providerPath}`;
}

/**
* Returns the fully-qualified HTTP endpoint the SDK will hit for `provider`,
* given an optional caller-supplied `baseUrl`. Returns `undefined` when no
* gateway is configured and the SDK would call the provider directly.
*
* Endpoint path per SDK:
* anthropic → <gatewayBase>/messages
* openai / openrouter → <gatewayBase>/chat/completions
* google via gateway → <gatewayRoot>/openai/v1/chat/completions (compat path)
* google direct → undefined (no fixed base URL)
*/
export function resolveRequestUrl(
provider: Provider,
baseUrl?: string,
): string | undefined {
const gatewayBase = baseUrl
? gatewayUrlFor(provider, baseUrl)
: getGatewayBaseURL(provider);
if (!gatewayBase) return undefined;

if (provider === "anthropic") {
return `${gatewayBase}/messages`;
}
if (provider === "google") {
// Mirror getModel(): strip any provider suffix to recover the gateway
// root, then target the OpenAI-compat endpoint.
const root = gatewayBase.replace(
/\/(anthropic|openai|genai)\/v\d+[a-z]*\/?$/i,
"",
);
return `${root}/openai/v1/chat/completions`;
}
// openai, openrouter
return `${gatewayBase}/chat/completions`;
}

/**
* Like {@link gatewayUrlFor} but takes a model name (shortcut like
* `"sonnet"`, namespaced like `"anthropic/claude-sonnet-5"`, or a full
Expand Down
2 changes: 2 additions & 0 deletions mcp/src/repo/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ModelName,
getModelDetails,
getProviderOptions,
resolveRequestUrl,
normalizeUsage,
} from "../aieo/src/index.js";
import { get_tools, ToolsConfig, SkillsConfig, GgnnConfig, MessagesRef, ProvenanceCollector } from "./tools.js";
Expand Down Expand Up @@ -556,6 +557,7 @@ Apply the guidance from each skill throughout your response.`;
),
providerConfig: getProviderOptions(provider as any, undefined, modelId),
baseUrl: opts.baseUrl,
requestUrl: resolveRequestUrl(provider as any, opts.baseUrl),
// Redact secrets before persisting
mcpServers: opts.mcpServers?.map(({ token, headers, ...rest }) => rest),
subAgents: opts.subAgents?.map(({ apiToken, ...rest }) => rest),
Expand Down
1 change: 1 addition & 0 deletions mcp/src/repo/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface SessionInitConfig {
tools?: Record<string, string>; // name → description for every resolved tool
providerConfig?: { [key: string]: any }; // resolved getProviderOptions output
baseUrl?: string;
requestUrl?: string;
mcpServers?: { [key: string]: any }[]; // secrets (token/headers) redacted
subAgents?: { [key: string]: any }[]; // secrets (apiToken) redacted
ggnn?: { [key: string]: any };
Expand Down
Loading