Agents
Overview
Use the Agents API to list, create, update, retrieve, and delete agents.
With this REST API, you can do the following:
List agents
Endpoints
GET aisvc/api/v1/agents
Request
Query parameters
| Parameter | Type | Description | Required |
|---|---|---|---|
_offset | Number | Number of results to skip | Optional |
_pageSize | Number | Number of results per page | Optional |
_userType | String | Filter by the agent's unique _userType identifier | Optional |
_type | String | Filter by the agent type, such as user_agent or system_agent | Optional |
query | String | Wildcard search on the name, description, and background of agents | Optional |
Request example
None
Response
Codes
| Code | Description |
|---|---|
200 | Success |
400 | Bad Request |
404 | Not Found |
Response body
{
"_offset": 0,
"_pageSize": 1,
"_total": 50,
"_list": [
{
"_name": "Related Items and File Helper Agent",
"_background": "It uses the RelatedQueryTool to generate related queries as JSON from the user prompt and feeds them to the SearchRelatedItemsTool to retrieve data from the Item Service. It can also look up file information using the getFiles_IafFileSvc system MCP tool and the GetFileNameById custom MCP tool.",
"_userType": "related_items_file_helper_agent",
"_namespaces": [
"iputGraph_kxYOcW7O"
],
"_config": {
"_model": "gpt-4o",
"_provider": "openai",
"_temperature": 1.2
},
"_tools": [
"RelatedQueryTool",
"SearchRelatedItemsTool",
"getFiles_IafFileSvc",
"GetFileNameById"
],
"_invocationMessage": "Analyzing related items and files…",
"_id": "6fe848f8-3d9b-48ff-bcf1-643756168904",
"_irn": "aisvc:agent:6fe848f8-3d9b-48ff-bcf1-643756168904",
"_metadata": {
"_createdAt": "1757048208583",
"_updatedById": "70699091-77de-4ee3-8a3f-034ec8746a3b",
"_createdById": "70699091-77de-4ee3-8a3f-034ec8746a3b",
"_updatedAt": "1757048208583"
}
}
]
}
Create agent
Endpoints
POST aisvc/api/v1/agents
Request
Request body
| Parameter | Type | Description | Required |
|---|---|---|---|
_namespaces | Array of String | One or more namespaces that contain the resource | Required |
_name | String | The agent's name | Required |
_background | String | Enter a prompt for prompt engineering. | Required |
_userType | String | Unique identifier of the agent | Required |
_description | String | Agent's description | Optional |
_tools | Array of String | Tools the agent can use. Each entry is either a regular tool's _userType or a Twinit MCP tool's _name — both system MCP tools (for example, getFiles_IafFileSvc) and custom (user) MCP tools (for example, GetFileNameById). See Twinit MCP tools (_tools) below. | Optional |
_knowledgebases | Array of String | The identifiers (_userType) of knowledge bases the agent uses | Optional |
_externalMcpServers | Array of Object | External MCP servers the agent can use (by _userType) | Optional |
_promptTemplate | String | The _userType of a Prompt Template to assign to this agent. The template serves as the agent's instruction and is parameterized at runtime. | Optional |
_invocationMessage | String | A short, plain-text status message streamed to the conversation when this agent is invoked during a team run (for example, Analyzing related items and files…). Maximum 500 characters (leading/trailing whitespace is trimmed). Must be plain text — HTML tags and template expressions ({{ }}, ${ }, <% %>) are not allowed. An empty or whitespace-only value is stored as null. | Optional |
_config | Object | The agent's LLM model configuration. See additional _config properties below. | Required |
_config._model | Object | The agent's LLM model. The set of available models is dynamic — use the LLM Models API to retrieve the supported model names for a provider. | Required |
_config._provider | Object | The provider of the agent's LLM model. Supported providers are openai and anthropic. | Required |
LLM configuration (_config)
| Parameter | Type | Description | Required |
|---|---|---|---|
_config._provider | String | The LLM provider (for example, openai or anthropic) | Required |
_config._model | String | The LLM model name (for example, gpt-4o) | Required |
_config._temperature | Number | Sampling temperature. Must be within the allowed range for the selected model. | |
If omitted or null, the service uses the model's default temperature. | Optional |
The allowed temperature range is model-specific, not provider-wide. Most OpenAI chat
models (for example gpt-4o, gpt-4.1, gpt-4-turbo) accept 0.0-2.0, and Anthropic
models accept 0.0-1.0.
Reasoning-tier models are the exception: the OpenAI o-series (o1, o3, o3-mini,
o4-mini) and the gpt-5 family (gpt-5, gpt-5-mini, gpt-5-nano,
gpt-5-chat-latest, gpt-5.1, gpt-5.1-mini, gpt-5.2, gpt-5.2-pro, gpt-5.5) support
only their default temperature of 1. This mirrors OpenAI's own restriction on those models.
Sending any other value — including 0.0 — returns 422:
{
"source": "ServiceException",
"message": "The _config._temperature is fixed at 1 for model 'gpt-5' and cannot be changed"
}
Omitting _config._temperature is always safe: the service applies the model's default, so
the same agent payload can be reused across models with different temperature rules. This is
the recommended approach when an agent's model may change, since the
LLM Models API reports model names only and does not return temperature
bounds.
Twinit MCP tools (_tools)
An agent can use Twinit MCP tools directly by listing the Twinit MCP tool's _name in the _tools array — there is no need to define or reference a Twinit MCP Server (_externalMcpServers) for this. Both kinds of Twinit MCP tools are supported:
| Type | _type value | Description | Reference by |
|---|---|---|---|
| System MCP tools | system_mcp_tool | Built-in, read-only tools for reading platform data. | _name, for example getFiles_IafFileSvc or getNamedUserItems_IafItemSvc |
| Custom (user) MCP tools | user_mcp_tool | Tools you create to run your own Item Service script. | _name you gave them, for example GetFileNameById |
Regular tools (by _userType) and Twinit MCP tools (by _name) can be mixed freely in the same _tools array.
Note: A Twinit MCP tool
_namemust not collide with an existing tool's_userType. If it does, agent creation fails with409 Conflictasking you to rename one of them.
MCP servers (_externalMcpServers)
Note: This field is stored internally on the agent resource as
external_mcp_servers(JSONB).
| Parameter | Type | Description | Required |
|---|---|---|---|
_externalMcpServers[]. _userType | String | External MCP server config _userType (see External MCP Server Config API). | Required |
_externalMcpServers[]. _filter | Object | Optional filter for MCP tool visibility. | Optional |
_externalMcpServers[]. _filter._names | Array of String | Optional list of MCP tool names to allow from this server. | Optional |
Request body example
[
{
"_name": "Related Items and File Helper Agent",
"_background": "It uses the RelatedQueryTool to generate related queries as JSON from the user prompt and feeds them to the SearchRelatedItemsTool to retrieve data from the Item Service. It can also look up file information using the getFiles_IafFileSvc system MCP tool and the GetFileNameById custom MCP tool.",
"_userType": "related_items_file_helper_agent",
"_namespaces": [
"iputGraph_kxYOcW7O"
],
"_config": {
"_model": "gpt-4o",
"_provider": "openai",
"_temperature": 1.5
},
"_tools": [
"RelatedQueryTool",
"SearchRelatedItemsTool",
"getFiles_IafFileSvc",
"GetFileNameById"
],
"_externalMcpServers": [
{
"_userType": "github_mcp",
"_filter": {
"_names": [
"search_repositories",
"list_issues"
]
}
}
],
"_promptTemplate": "platform_resource_universal_tmpl",
"_invocationMessage": "Analyzing related items and files…"
}
]
Response
Codes
| Code | Description |
|---|---|
200 | Success |
400 | Bad Request |
404 | Not Found |
422 | Unprocessable Entity |
Response body
{
"_offset": 0,
"_pageSize": 1,
"_total": 1,
"_list": [
{
"_name": "Related Items and File Helper Agent",
"_background": "It uses the RelatedQueryTool to generate related queries as JSON from the user prompt and feeds them to the SearchRelatedItemsTool to retrieve data from the Item Service. It can also look up file information using the getFiles_IafFileSvc system MCP tool and the GetFileNameById custom MCP tool.",
"_userType": "related_items_file_helper_agent",
"_namespaces": [
"iputGraph_kxYOcW7O"
],
"_config": {
"_model": "gpt-4o",
"_provider": "openai",
"_temperature": 1.5
},
"_tools": [
"RelatedQueryTool",
"SearchRelatedItemsTool",
"getFiles_IafFileSvc",
"GetFileNameById"
],
"_promptTemplate": "platform_resource_universal_tmpl",
"_invocationMessage": "Analyzing related items and files…",
"_id": "6fe848f8-3d9b-48ff-bcf1-643756168904",
"_irn": "aisvc:agent:6fe848f8-3d9b-48ff-bcf1-643756168904",
"_metadata": {
"_createdAt": "1757048208583",
"_updatedById": "70699091-77de-4ee3-8a3f-034ec8746a3b",
"_createdById": "70699091-77de-4ee3-8a3f-034ec8746a3b",
"_updatedAt": "1757048208583"
}
}
]
}
Get agent by ID
Endpoints
GET aisvc/api/v1/agents/{id}
Response
Codes
| Code | Description |
|---|---|
200 | Success |
400 | Bad Request |
404 | Not Found |
Response body
{
"_name": "Related Items and File Helper Agent",
"_background": "It uses the RelatedQueryTool to generate related queries as JSON from the user prompt and feeds them to the SearchRelatedItemsTool to retrieve data from the Item Service. It can also look up file information using the getFiles_IafFileSvc system MCP tool and the GetFileNameById custom MCP tool.",
"_userType": "related_items_file_helper_agent",
"_namespaces": [
"iputGraph_kxYOcW7O"
],
"_config": {
"_model": "gpt-4o",
"_provider": "openai",
"_temperature": 1.5
},
"_tools": [
"RelatedQueryTool",
"SearchRelatedItemsTool",
"getFiles_IafFileSvc",
"GetFileNameById"
],
"_invocationMessage": "Analyzing related items and files…",
"_id": "6fe848f8-3d9b-48ff-bcf1-643756168904",
"_irn": "aisvc:agent:6fe848f8-3d9b-48ff-bcf1-643756168904",
"_metadata": {
"_createdAt": "1757048208583",
"_updatedById": "70699091-77de-4ee3-8a3f-034ec8746a3b",
"_createdById": "70699091-77de-4ee3-8a3f-034ec8746a3b",
"_updatedAt": "1757048208583"
}
}
Update agent by ID
Endpoints
PUT aisvc/api/v1/agents/{id}
Request
Request body
| Parameter | Type | Description | Required |
|---|---|---|---|
_name | String | The agent name | Required |
_background | String | The context for the agent | Required |
_userType | String | Unique identifier of the agent | Required |
_description | String | Agent's description | Optional |
_tools | Array of String | Tools usable by the agent. Each entry is either a regular tool's _userType or a Twinit MCP tool's _name — both system MCP tools (for example, getFiles_IafFileSvc) and custom (user) MCP tools (for example, GetFileNameById). See Twinit MCP tools (_tools) above. | Optional |
_knowledgebases | Array of String | The identifiers (_userType) of knowledge bases usable by agent | Optional |
_externalMcpServers | Array of Object | External MCP servers the agent can use (by _userType) | Optional |
_promptTemplate | String | The _userType of a Prompt Template to assign to this agent. The template serves as the agent's instruction and is parameterized at runtime. | Optional |
_invocationMessage | String | A short, plain-text status message streamed to the conversation when this agent is invoked during a team run (for example, Analyzing related items and files…). Maximum 500 characters (leading/trailing whitespace is trimmed). Must be plain text — HTML tags and template expressions ({{ }}, ${ }, <% %>) are not allowed. An empty or whitespace-only value is stored as null. | Optional |
_config | Object | Configuration for the agent's LLM model | Required |
LLM configuration (_config)
| Parameter | Type | Description | Required |
|---|---|---|---|
_config._provider | String | The LLM provider (for example, openai or anthropic) | Required |
_config._model | String | The LLM model name (for example, gpt-4o) | Required |
_config._temperature | Number | Sampling temperature. Must be within the allowed range for the selected model. If omitted or null, the service uses the model's default temperature. | Optional |
MCP servers (_externalMcpServers)
Note: This field is stored internally on the agent resource as
external_mcp_servers(JSONB).
| Parameter | Type | Description | Required |
|---|---|---|---|
_externalMcpServers[]. _userType | String | External MCP server config _userType (see External MCP Server Config API). | Required |
_externalMcpServers[]. _filter | Object | Optional filter for MCP tool visibility. | Optional |
_externalMcpServers[]. _filter._names | Array of String | Optional list of MCP tool names to allow from this server. | Optional |
Request body example
{
"_name": "Related Items and File Helper Agent",
"_background": "It uses the RelatedQueryTool to generate related queries as JSON from the user prompt and feeds them to the SearchRelatedItemsTool to retrieve data from the Item Service. It can also look up file information using the getFiles_IafFileSvc system MCP tool and the GetFileNameById custom MCP tool.",
"_userType": "related_items_file_helper_agent",
"_namespaces": [
"iputGraph_kxYOcW7O"
],
"_config": {
"_model": "gpt-4o",
"_provider": "openai",
"_temperature": 1.5
},
"_tools": [
"RelatedQueryTool",
"SearchRelatedItemsTool",
"getFiles_IafFileSvc",
"GetFileNameById"
],
"_externalMcpServers": [
{
"_userType": "github_mcp",
"_filter": {
"_names": [
"search_repositories",
"list_issues"
]
}
}
],
"_promptTemplate": "platform_resource_universal_tmpl",
"_invocationMessage": "Analyzing related items and files…"
}
Response
Codes
| Code | Description |
|---|---|
200 | Success |
400 | Bad Request |
404 | Not Found |
422 | Unprocessable Entity |
Response body
{
"_name": "Related Items and File Helper Agent",
"_background": "It uses the RelatedQueryTool to generate related queries as JSON from the user prompt and feeds them to the SearchRelatedItemsTool to retrieve data from the Item Service. It can also look up file information using the getFiles_IafFileSvc system MCP tool and the GetFileNameById custom MCP tool.",
"_userType": "related_items_file_helper_agent",
"_namespaces": [
"iputGraph_kxYOcW7O"
],
"_config": {
"_model": "gpt-4o",
"_provider": "openai",
"_temperature": 1.5
},
"_tools": [
"RelatedQueryTool",
"SearchRelatedItemsTool",
"getFiles_IafFileSvc",
"GetFileNameById"
],
"_promptTemplate": "platform_resource_universal_tmpl",
"_invocationMessage": "Analyzing related items and files…",
"_id": "6fe848f8-3d9b-48ff-bcf1-643756168904",
"_irn": "aisvc:agent:6fe848f8-3d9b-48ff-bcf1-643756168904",
"_metadata": {
"_createdAt": "1757048208583",
"_updatedById": "70699091-77de-4ee3-8a3f-034ec8746a3b",
"_createdById": "70699091-77de-4ee3-8a3f-034ec8746a3b",
"_updatedAt": "1757048208583"
}
}
Delete agent by ID
Endpoints
DELETE aisvc/api/v1/agents/{id}
Request
Request body
None
Response
Codes
| Code | Description |
|---|---|
200 | Success |
400 | Bad Request |
404 | Not Found |
Response body
// empty response body
<!-- Hidden per PLN-2761: custom agents run in the AI Service process space and are no longer documented. Use custom MCP tools instead.
Upload Agent Source Code
Endpoints
PUT aisvc/api/v1/agents/{id}/sourcecodes
Request
Request body
{
"_content":"import { Agent } from '@dtplatform/agent-core';\nimport { IafItemSvc } from '@dtplatform/platform-api';\n\ninterface ChillerRequest {\n chillerId: string;\n capacityKw: number;\n metric: string; // e.g. \"chiller_load_kw\"\n thresholdPct: number; // e.g. 0.9 for 90%\n lookbackDays?: number;\n}\n\ntype Reading = {\n _id: string;\n _ts: string; // timestamp in UTC\n chiller_load_kw: number;\n chiller_load_pct: number;\n};\n\ntype Data = {\n _list: Reading[];\n};\n\nexport default class ChillerForecastAgent extends Agent {\n requestContext: any = {}\n constructor(options: any, requestContext: any) {\n super(options);\n this.requestContext = requestContext;\n }\n\n async processRequest(\n type: string,\n state?: any,\n prompt?: string,\n tools?: any[],\n userId?: string,\n sessionId?: string,\n chatHistory?: [],\n additionalParams?: Record<string, string>,\n ): Promise<any> {\n\n // ------------------------------\n // 1. Interpret prompt (simple parsing via LLM)\n // ------------------------------\n const llmRes: any = await this.simpleCall(`Extract structured JSON from the following prompt. \n Fields: chillerId and lookbackDays (optional).\n Prompt: ${prompt}`);\n\n //console.log(llmRes, \">>llmRes\")\n console.log(llmRes?.content, \">>llmRes?.content\")\n\n let chillerReq: ChillerRequest;\n\n try {\n let rawContent: string = llmRes?.content || \"\";\n\n // Remove Markdown code fences if they exist\n rawContent = rawContent.replace(/```json|```/gi, \"\").trim();\n\n // Try to extract JSON block if extra text is present\n const jsonMatch = rawContent.match(/\{[\s\S]*\}/);\n if (jsonMatch) {\n rawContent = jsonMatch[0];\n }\n\n chillerReq = JSON.parse(rawContent);\n\n } catch (err) {\n console.error(\"Failed to parse chiller request:\", err);\n return {\n messages: [\n {\n role: \"assistant\",\n content: JSON.stringify({ error: \"Invalid LLM JSON response\", raw: llmRes })\n }\n ]\n };\n }\n\n const lookback = chillerReq.lookbackDays ?? 90;\n const startDate = new Date();\n startDate.setDate(startDate.getDate() - lookback);\n\n const aggs = [\n {\n $match: {\n \"_tsMetadata._sourceId\": { \"$regex\": `.*${chillerReq.chillerId}.*` } ,\n \"_ts\": { $gte: startDate.toISOString() }\n }\n },\n { $sort: { ts: 1 } },\n { $limit: 10 }\n\n ];\n\n console.log('Aggs::', JSON.stringify(aggs))\n \n let res = await IafItemSvc.aggregateReadings('68c50a58993fee0ea750db6f', aggs, this.requestContext, {});\n let finalRes = this.predictChiller90(res);\n console.log(finalRes, 'finalRes')\n return {\n messages: [\n {\n role: \"assistant\",\n content: `Chiller will cross 90% load around:\", ${finalRes}`\n }\n ]\n };\n }\n\n\n predictChiller90(data: Data): Date | null {\n // Step 1: clean duplicates by unique timestamp\n const readings: Reading[] = Object.values(\n data._list.reduce<Record<string, Reading>>((acc, item) => {\n acc[item._ts] = item; // overwrite duplicates\n return acc;\n }, {})\n );\n\n if (readings.length < 2) return null; // not enough data to predict\n\n // Step 2: sort by timestamp\n readings.sort((a, b) => new Date(a._ts).getTime() - new Date(b._ts).getTime());\n\n // Step 3: convert timestamps to epoch ms\n const points = readings.map(r => ({\n ts: new Date(r._ts).getTime(),\n load: r.chiller_load_pct\n }));\n\n // Step 4: calculate slope & intercept (linear regression)\n const n = points.length;\n const sumX = points.reduce((s, p) => s + p.ts, 0);\n const sumY = points.reduce((s, p) => s + p.load, 0);\n const sumXY = points.reduce((s, p) => s + p.ts * p.load, 0);\n const sumX2 = points.reduce((s, p) => s + p.ts * p.ts, 0);\n\n const denominator = n * sumX2 - sumX * sumX;\n if (denominator === 0) return null; // avoid division by zero\n\n const slope = (n * sumXY - sumX * sumY) / denominator;\n const intercept = (sumY - slope * sumX) / n;\n\n // Step 5: solve for timestamp when load = 0.9\n const targetLoad = 0.9;\n const targetTs = (targetLoad - intercept) / slope;\n\n if (isNaN(targetTs) || !isFinite(targetTs)) return null;\n\n return new Date(targetTs);\n }\n\n}\n"
}
Response
Codes
| Code | Description |
|---|---|
200 | Success |
400 | Bad Request |
404 | Not Found |
Get Agent Source Code
Endpoints
GET aisvc/api/v1/agents/{id}/sourcecodes
Response
Codes
| Code | Description |
|---|---|
200 | Success |
400 | Bad Request |
404 | Not Found |
Response body
[
{
"_content": "import { Agent } from '@dtplatform/agent-core';\nimport { IafItemSvc } from '@dtplatform/platform-api';\n\ninterface ChillerRequest {\n chillerId: string;\n capacityKw: number;\n metric: string; // e.g. \"chiller_load_kw\"\n thresholdPct: number; // e.g. 0.9 for 90%\n lookbackDays?: number;\n}\n\ntype Reading = {\n _id: string;\n _ts: string; // timestamp in UTC\n chiller_load_kw: number;\n chiller_load_pct: number;\n};\n\ntype Data = {\n _list: Reading[];\n};\n\nexport default class ChillerForecastAgent extends Agent {\n requestContext: any = {}\n constructor(options: any, requestContext: any) {\n super(options);\n this.requestContext = requestContext;\n }\n\n async processRequest(\n type: string,\n state?: any,\n prompt?: string,\n tools?: any[],\n userId?: string,\n sessionId?: string,\n chatHistory?: [],\n additionalParams?: Record<string, string>,\n ): Promise<any> {\n \n\n // ------------------------------\n // 1. Interpret prompt (simple parsing via LLM)\n // ------------------------------\n const llmRes: any = await this.simpleCall(`Extract structured JSON from the following prompt. \n Fields: chillerId and lookbackDays (optional).\n Prompt: ${prompt}`);\n\n //console.log(llmRes, \">>llmRes\")\n console.log(llmRes?.content, \">>llmRes?.content\")\n\n let chillerReq: ChillerRequest;\n\n try {\n let rawContent: string = llmRes?.content || \"\";\n\n // Remove Markdown code fences if they exist\n rawContent = rawContent.replace(/```json|```/gi, \"\").trim();\n\n // Try to extract JSON block if extra text is present\n const jsonMatch = rawContent.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n rawContent = jsonMatch[0];\n }\n\n chillerReq = JSON.parse(rawContent);\n\n } catch (err) {\n console.error(\"Failed to parse chiller request:\", err);\n return {\n messages: [\n {\n role: \"assistant\",\n content: JSON.stringify({ error: \"Invalid LLM JSON response\", raw: llmRes })\n }\n ]\n };\n }\n\n // ------------------------------\n // 2. Query Related Items\n // ------------------------------\n const telItems = await IafItemSvc.getRelatedItems(\n '68c50a58993fee0ea750db6f',\n { query: { \"_sourceId\": { \"$regex\": `.*${chillerReq.chillerId}.*` } } },\n this.requestContext\n );\n\n const lookback = chillerReq.lookbackDays ?? 90;\n const startDate = new Date();\n startDate.setDate(startDate.getDate() - lookback);\n\n const aggs = [\n {\n $match: {\n \"_tsMetadata._sourceId\": { \"$regex\": `.*${chillerReq.chillerId}.*` } ,\n \"_ts\": { $gte: startDate.toISOString() }\n }\n },\n { $sort: { ts: 1 } },\n { $limit: 10 }\n\n ];\n\n console.log('Aggs::', JSON.stringify(aggs))\n \n let res = await IafItemSvc.aggregateReadings('68c50a58993fee0ea750db6f', aggs, this.requestContext, {});\n let finalRes = this.predictChiller90(res);\n console.log(finalRes, 'finalRes')\n return {\n messages: [\n {\n role: \"assistant\",\n content: `Chiller will cross 90% load around:\", ${finalRes}`\n }\n ]\n };\n }\n\n\n predictChiller90(data: Data): Date | null {\n // Step 1: clean duplicates by unique timestamp\n const readings: Reading[] = Object.values(\n data._list.reduce<Record<string, Reading>>((acc, item) => {\n acc[item._ts] = item; // overwrite duplicates\n return acc;\n }, {})\n );\n\n if (readings.length < 2) return null; // not enough data to predict\n\n // Step 2: sort by timestamp\n readings.sort((a, b) => new Date(a._ts).getTime() - new Date(b._ts).getTime());\n\n // Step 3: convert timestamps to epoch ms\n const points = readings.map(r => ({\n ts: new Date(r._ts).getTime(),\n load: r.chiller_load_pct\n }));\n\n // Step 4: calculate slope & intercept (linear regression)\n const n = points.length;\n const sumX = points.reduce((s, p) => s + p.ts, 0);\n const sumY = points.reduce((s, p) => s + p.load, 0);\n const sumXY = points.reduce((s, p) => s + p.ts * p.load, 0);\n const sumX2 = points.reduce((s, p) => s + p.ts * p.ts, 0);\n\n const denominator = n * sumX2 - sumX * sumX;\n if (denominator === 0) return null; // avoid division by zero\n\n const slope = (n * sumXY - sumX * sumY) / denominator;\n const intercept = (sumY - slope * sumX) / n;\n\n // Step 5: solve for timestamp when load = 0.9\n const targetLoad = 0.9;\n const targetTs = (targetLoad - intercept) / slope;\n\n if (isNaN(targetTs) || !isFinite(targetTs)) return null;\n\n return new Date(targetTs);\n }\n\n}\n"
}
]
-->