Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
6 changes: 6 additions & 0 deletions src/bin/cmd-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as readline from "readline";
import { CLIAgent, type Provider } from "../clients/cli-agent.js";
import { MultiIndexRunner } from "../clients/multi-index-runner.js";
import { CompositeStoreReader, parseIndexSpecs } from "../stores/index.js";
import { buildClientUserAgent } from "../core/utils.js";

const PROVIDER_DEFAULTS: Record<Provider, string> = {
openai: "gpt-5-mini",
Expand Down Expand Up @@ -50,9 +51,13 @@ export const agentCommand = new Command("agent")
const store = await CompositeStoreReader.fromSpecs(specs);

// Create multi-index runner
// Build User-Agent for analytics tracking
const clientUserAgent = buildClientUserAgent("cli-agent");

const runner = await MultiIndexRunner.create({
store,
searchOnly: options.searchOnly,
clientUserAgent,
});

console.log("\x1b[1;36mContext Connectors Minimal Agent\x1b[0m");
Expand All @@ -73,6 +78,7 @@ export const agentCommand = new Command("agent")
model,
maxSteps: options.maxSteps,
verbose: options.verbose,
clientUserAgent,
});
await agent.initialize();

Expand Down
5 changes: 4 additions & 1 deletion src/bin/cmd-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Indexer } from "../core/indexer.js";
import { Source } from "../sources/types.js";
import { FilesystemStore } from "../stores/filesystem.js";
import { getS3Config } from "../stores/s3-config.js";
import { buildClientUserAgent } from "../core/utils.js";

// Shared store options
interface StoreOptions {
Expand Down Expand Up @@ -49,7 +50,9 @@ async function runIndex(
sourceType: string
) {
console.log(`Indexing ${sourceType} source...`);
const indexer = new Indexer();
const indexer = new Indexer({
clientUserAgent: buildClientUserAgent("cli-index"),
});
const result = await indexer.index(source, store, indexKey);

console.log(`\nIndexing complete!`);
Expand Down
2 changes: 2 additions & 0 deletions src/bin/cmd-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getSourceIdentifier } from "../core/types.js";
import { getS3Config } from "../stores/s3-config.js";
import { parseIndexSpec } from "../stores/index-spec.js";
import type { IndexStoreReader } from "../stores/types.js";
import { buildClientUserAgent } from "../core/utils.js";

export const searchCommand = new Command("search")
.description("Search indexed content and answer questions (use --raw for raw results)")
Expand Down Expand Up @@ -83,6 +84,7 @@ export const searchCommand = new Command("search")
const client = new SearchClient({
store,
indexName: indexKey,
clientUserAgent: buildClientUserAgent("cli-search"),
});

await client.initialize();
Expand Down
20 changes: 18 additions & 2 deletions src/clients/cli-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@
stream?: boolean;
/** Custom system prompt. Uses a sensible default if not provided. */
systemPrompt?: string;
/**
* Custom User-Agent string for analytics tracking.
* When provided, this is sent to the Augment API for usage analytics.
* Only used when provider is 'augment'.
*/
clientUserAgent?: string;
}

/**
Expand Down Expand Up @@ -110,6 +116,12 @@
stream?: boolean;
/** Custom system prompt. Uses a sensible default if not provided. */
systemPrompt?: string;
/**
* Custom User-Agent string for analytics tracking.
* When provided, this is sent to the Augment API for usage analytics.
* Only used when provider is 'augment'.
*/
clientUserAgent?: string;
}

/** Configuration for the CLI agent */
Expand All @@ -136,7 +148,8 @@
*/
async function loadModel(
provider: Provider,
modelName: string
modelName: string,
clientUserAgent?: string
): Promise<LanguageModel> {
switch (provider) {
case "openai": {
Expand Down Expand Up @@ -179,6 +192,7 @@
return new AugmentLanguageModel(modelName, {
apiKey: credentials.apiKey,
apiUrl: credentials.apiUrl,
clientUserAgent,

Check failure on line 195 in src/clients/cli-agent.ts

View workflow job for this annotation

GitHub Actions / test

Object literal may only specify known properties, and 'clientUserAgent' does not exist in type 'AugmentLanguageModelConfig'.
}) as unknown as LanguageModel;
}
default:
Expand Down Expand Up @@ -223,6 +237,7 @@
private readonly verbose: boolean;
private readonly stream: boolean;
private readonly systemPrompt: string;
private readonly clientUserAgent?: string;
private readonly tools: ToolSet;
private messages: CoreMessage[] = [];

Expand All @@ -242,6 +257,7 @@
this.verbose = config.verbose ?? false;
this.stream = config.stream ?? true;
this.systemPrompt = config.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
this.clientUserAgent = config.clientUserAgent;
this.tools = this.runner ? this.createMultiIndexTools() : this.createSingleClientTools();
}

Expand Down Expand Up @@ -393,7 +409,7 @@
* @throws Error if the provider package is not installed
*/
async initialize(): Promise<void> {
this.model = await loadModel(this.provider, this.modelName);
this.model = await loadModel(this.provider, this.modelName, this.clientUserAgent);
}

/**
Expand Down
5 changes: 5 additions & 0 deletions src/clients/mcp-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
} from "@modelcontextprotocol/sdk/types.js";
import type { IndexStoreReader } from "../stores/types.js";
import { MultiIndexRunner } from "./multi-index-runner.js";
import { buildClientUserAgent } from "../core/utils.js";
import {
SEARCH_DESCRIPTION,
LIST_FILES_DESCRIPTION,
Expand Down Expand Up @@ -96,10 +97,14 @@ export async function createMCPServer(
config: MCPServerConfig
): Promise<Server> {
// Create shared runner for multi-index operations
// Build User-Agent for analytics tracking
const clientUserAgent = buildClientUserAgent("mcp");

const runner = await MultiIndexRunner.create({
store: config.store,
indexNames: config.indexNames,
searchOnly: config.searchOnly,
clientUserAgent,
});

const { indexNames, indexes } = runner;
Expand Down
13 changes: 11 additions & 2 deletions src/clients/multi-index-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export interface MultiIndexRunnerConfig {
* When true, only search is available.
*/
searchOnly?: boolean;
/**
* Custom User-Agent string for analytics tracking.
* When provided, this is passed to SearchClient instances for API requests.
*/
clientUserAgent?: string;
}

/** Create a Source from index state metadata */
Expand Down Expand Up @@ -65,6 +70,7 @@ async function createSourceFromState(state: IndexStateSearchOnly): Promise<Sourc
export class MultiIndexRunner {
private readonly store: IndexStoreReader;
private readonly searchOnly: boolean;
private readonly clientUserAgent?: string;
private readonly clientCache = new Map<string, SearchClient>();

/** Available index names */
Expand All @@ -77,12 +83,14 @@ export class MultiIndexRunner {
store: IndexStoreReader,
indexNames: string[],
indexes: IndexInfo[],
searchOnly: boolean
searchOnly: boolean,
clientUserAgent?: string
) {
this.store = store;
this.indexNames = indexNames;
this.indexes = indexes;
this.searchOnly = searchOnly;
this.clientUserAgent = clientUserAgent;
}

/**
Expand Down Expand Up @@ -132,7 +140,7 @@ export class MultiIndexRunner {
throw new Error("No valid indexes available (all indexes failed to load)");
}

return new MultiIndexRunner(store, validIndexNames, indexes, searchOnly);
return new MultiIndexRunner(store, validIndexNames, indexes, searchOnly, config.clientUserAgent);
}

/**
Expand All @@ -158,6 +166,7 @@ export class MultiIndexRunner {
store: this.store,
source,
indexName,
clientUserAgent: this.clientUserAgent,
});
await client.initialize();
this.clientCache.set(indexName, client);
Expand Down
8 changes: 8 additions & 0 deletions src/clients/search-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@
* @default process.env.AUGMENT_API_URL
*/
apiUrl?: string;
/**
* Custom User-Agent string for analytics tracking.
* When provided, this is sent to the Augment API for usage analytics.
*/
clientUserAgent?: string;
}

/**
Expand Down Expand Up @@ -106,6 +111,7 @@
private indexName: string;
private apiKey: string;
private apiUrl: string;
private clientUserAgent?: string;

private context: DirectContext | null = null;
private state: IndexStateSearchOnly | null = null;
Expand All @@ -123,6 +129,7 @@
this.indexName = config.indexName;
this.apiKey = config.apiKey ?? process.env.AUGMENT_API_TOKEN ?? "";
this.apiUrl = config.apiUrl ?? process.env.AUGMENT_API_URL ?? "";
this.clientUserAgent = config.clientUserAgent;
}

/**
Expand Down Expand Up @@ -162,6 +169,7 @@
this.context = await DirectContext.import(this.state.contextState, {
apiKey: this.apiKey,
apiUrl: this.apiUrl,
clientUserAgent: this.clientUserAgent,

Check failure on line 172 in src/clients/search-client.ts

View workflow job for this annotation

GitHub Actions / test

Object literal may only specify known properties, and 'clientUserAgent' does not exist in type 'DirectContextOptions'.
});
}

Expand Down
3 changes: 2 additions & 1 deletion src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export {
shouldFilterFile,
} from "./file-filter.js";

export { sanitizeKey, isoTimestamp } from "./utils.js";
export { sanitizeKey, isoTimestamp, buildClientUserAgent } from "./utils.js";
export type { ClientInterface, MCPClientInfo } from "./utils.js";

export { Indexer } from "./indexer.js";
export type { IndexerConfig } from "./indexer.js";
Expand Down
9 changes: 9 additions & 0 deletions src/core/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@
* @default process.env.AUGMENT_API_URL
*/
apiUrl?: string;
/**
* Custom User-Agent string for analytics tracking.
* When provided, this is sent to the Augment API for usage analytics.
*/
clientUserAgent?: string;
}

/**
Expand Down Expand Up @@ -82,6 +87,7 @@
export class Indexer {
private readonly apiKey?: string;
private readonly apiUrl?: string;
private readonly clientUserAgent?: string;

/**
* Create a new Indexer instance.
Expand All @@ -91,6 +97,7 @@
constructor(config: IndexerConfig = {}) {
this.apiKey = config.apiKey ?? process.env.AUGMENT_API_TOKEN;
this.apiUrl = config.apiUrl ?? process.env.AUGMENT_API_URL;
this.clientUserAgent = config.clientUserAgent;
}

/**
Expand Down Expand Up @@ -191,6 +198,7 @@
const context = await DirectContext.create({
apiKey: this.apiKey,
apiUrl: this.apiUrl,
clientUserAgent: this.clientUserAgent,

Check failure on line 201 in src/core/indexer.ts

View workflow job for this annotation

GitHub Actions / test

Object literal may only specify known properties, and 'clientUserAgent' does not exist in type 'DirectContextOptions'.
});

// Fetch all files from source
Expand Down Expand Up @@ -254,6 +262,7 @@
const context = await DirectContext.import(previousState.contextState, {
apiKey: this.apiKey,
apiUrl: this.apiUrl,
clientUserAgent: this.clientUserAgent,

Check failure on line 265 in src/core/indexer.ts

View workflow job for this annotation

GitHub Actions / test

Object literal may only specify known properties, and 'clientUserAgent' does not exist in type 'DirectContextOptions'.
});

// Remove deleted files
Expand Down
46 changes: 46 additions & 0 deletions src/core/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Tests for utility functions
*/

import { describe, it, expect } from "vitest";
import { buildClientUserAgent } from "./utils.js";

describe("buildClientUserAgent", () => {
it("should build basic user agent for cli-search", () => {
const ua = buildClientUserAgent("cli-search");
expect(ua).toMatch(/^context-connectors\/\d+\.\d+\.\d+ via:cli-search$/);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These assertions only accept strict x.y.z; if package.json ever uses pre-release/build metadata (e.g. 0.1.3-beta.1) the tests will fail even though buildClientUserAgent() is behaving correctly.

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

});

it("should build basic user agent for cli-index", () => {
const ua = buildClientUserAgent("cli-index");
expect(ua).toMatch(/^context-connectors\/\d+\.\d+\.\d+ via:cli-index$/);
});

it("should build basic user agent for mcp", () => {
const ua = buildClientUserAgent("mcp");
expect(ua).toMatch(/^context-connectors\/\d+\.\d+\.\d+ via:mcp$/);
});

it("should include MCP client info when provided", () => {
const ua = buildClientUserAgent("mcp", { name: "claude-desktop", version: "1.2.0" });
expect(ua).toMatch(/^context-connectors\/\d+\.\d+\.\d+ via:mcp client:claude-desktop\/1\.2\.0$/);
});

it("should handle all interface types", () => {
const interfaces = [
"cli-search",
"sdk-search",
"cli-index",
"sdk-index",
"mcp",
"cli-agent",
"sdk-agent-provider",
] as const;

for (const iface of interfaces) {
const ua = buildClientUserAgent(iface);
expect(ua).toContain(`via:${iface}`);
expect(ua).toContain("context-connectors/");
}
});
});
Loading
Loading