lemura 1.5.0 → 1.5.2
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/CHANGELOG.md +36 -0
- package/dist/adapters/index.d.mts +7 -1
- package/dist/adapters/index.d.ts +7 -1
- package/dist/adapters/index.js +17 -3
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/index.mjs +17 -3
- package/dist/adapters/index.mjs.map +1 -1
- package/dist/{adapters-DHQOGl8Y.d.ts → adapters-CIRkrCHl.d.ts} +2 -0
- package/dist/{adapters-S96Ka9wt.d.mts → adapters-DAzmrg4l.d.mts} +2 -0
- package/dist/{agent-CjoFQGlk.d.ts → agent-Cq_oRvoc.d.ts} +10 -2
- package/dist/{agent-BefjpRHZ.d.mts → agent-DPSUNlK6.d.mts} +10 -2
- package/dist/context/index.d.mts +2 -2
- package/dist/context/index.d.ts +2 -2
- package/dist/index.d.mts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +220 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +220 -33
- package/dist/index.mjs.map +1 -1
- package/dist/skills/index.js +2 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/index.mjs +2 -1
- package/dist/skills/index.mjs.map +1 -1
- package/dist/tools/index.d.mts +2 -2
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js +18 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/index.mjs +18 -4
- package/dist/tools/index.mjs.map +1 -1
- package/dist/types/index.d.mts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as IProviderAdapter, d as TranscriptionRequest, e as TranscriptionResponse, f as SynthesisRequest, A as AudioChunk, V as VisionRequest, g as VisionResponse, h as ImageGenRequest, i as ImageGenResponse, M as ModelInfo, C as ContextWindow, T as Turn, j as ContentBlock, I as IToolDefinition } from './adapters-
|
|
2
|
-
export { k as CompletionChunk, l as CompletionRequest, m as CompletionResponse, b as IContextStrategy, c as IScratchpadAdapter, n as IStorageAdapter, N as NormalizedMessage, o as STMItem, p as STMRegistryConfig, S as ShortTermMemoryRegistry, q as TokenUsage, r as ToolCall, s as ToolContext, t as ToolResult } from './adapters-
|
|
3
|
-
import { S as SessionConfig, G as Goal, I as IToolResponseProcessor, T as ToolResponseEvaluation, M as MCPServerConfig, a as MCPToolDefinition } from './agent-
|
|
4
|
-
export { b as GoalInjector, c as GoalVerifierResult, d as MCPJsonRpcRequest, e as MCPJsonRpcResponse, f as MCPTransportType, g as MediaConfig, h as ToolDecision, i as ToolExecutionBudget, j as ToolFirewallConfig, k as ToolFirewallRule, l as TraceEvent } from './agent-
|
|
1
|
+
import { a as IProviderAdapter, d as TranscriptionRequest, e as TranscriptionResponse, f as SynthesisRequest, A as AudioChunk, V as VisionRequest, g as VisionResponse, h as ImageGenRequest, i as ImageGenResponse, M as ModelInfo, C as ContextWindow, T as Turn, j as ContentBlock, I as IToolDefinition } from './adapters-DAzmrg4l.mjs';
|
|
2
|
+
export { k as CompletionChunk, l as CompletionRequest, m as CompletionResponse, b as IContextStrategy, c as IScratchpadAdapter, n as IStorageAdapter, N as NormalizedMessage, o as STMItem, p as STMRegistryConfig, S as ShortTermMemoryRegistry, q as TokenUsage, r as ToolCall, s as ToolContext, t as ToolResult } from './adapters-DAzmrg4l.mjs';
|
|
3
|
+
import { S as SessionConfig, G as Goal, I as IToolResponseProcessor, T as ToolResponseEvaluation, M as MCPServerConfig, a as MCPToolDefinition } from './agent-DPSUNlK6.mjs';
|
|
4
|
+
export { b as GoalInjector, c as GoalVerifierResult, d as MCPJsonRpcRequest, e as MCPJsonRpcResponse, f as MCPTransportType, g as MediaConfig, h as ToolDecision, i as ToolExecutionBudget, j as ToolFirewallConfig, k as ToolFirewallRule, l as TraceEvent } from './agent-DPSUNlK6.mjs';
|
|
5
5
|
export { LemuraAdapterError, LemuraContextOverflowError, LemuraError, LemuraMCPConnectionError, LemuraMCPError, LemuraMCPTimeoutError, LemuraMaxIterationsError, LemuraSkillInjectionError, LemuraToolNotFoundError, LemuraToolTimeoutError, LemuraToolValidationError } from './types/index.mjs';
|
|
6
6
|
import { I as ILogger } from './logger-DxvKliuk.mjs';
|
|
7
7
|
export { L as LogLevel, a as LogMetadata, S as Severity } from './logger-DxvKliuk.mjs';
|
|
@@ -226,6 +226,8 @@ declare class SessionManager {
|
|
|
226
226
|
private toolResponseProcessor;
|
|
227
227
|
private goalInjector;
|
|
228
228
|
private continuationPlanner;
|
|
229
|
+
/** Frozen goal/plan injection text keyed by turn index — used when staticSystemPrompt is on */
|
|
230
|
+
private _turnInjections;
|
|
229
231
|
private mcpRegistry;
|
|
230
232
|
/** Resolves when all MCP servers are connected; awaited by run() and stream() */
|
|
231
233
|
private mcpReady;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as IProviderAdapter, d as TranscriptionRequest, e as TranscriptionResponse, f as SynthesisRequest, A as AudioChunk, V as VisionRequest, g as VisionResponse, h as ImageGenRequest, i as ImageGenResponse, M as ModelInfo, C as ContextWindow, T as Turn, j as ContentBlock, I as IToolDefinition } from './adapters-
|
|
2
|
-
export { k as CompletionChunk, l as CompletionRequest, m as CompletionResponse, b as IContextStrategy, c as IScratchpadAdapter, n as IStorageAdapter, N as NormalizedMessage, o as STMItem, p as STMRegistryConfig, S as ShortTermMemoryRegistry, q as TokenUsage, r as ToolCall, s as ToolContext, t as ToolResult } from './adapters-
|
|
3
|
-
import { S as SessionConfig, G as Goal, I as IToolResponseProcessor, T as ToolResponseEvaluation, M as MCPServerConfig, a as MCPToolDefinition } from './agent-
|
|
4
|
-
export { b as GoalInjector, c as GoalVerifierResult, d as MCPJsonRpcRequest, e as MCPJsonRpcResponse, f as MCPTransportType, g as MediaConfig, h as ToolDecision, i as ToolExecutionBudget, j as ToolFirewallConfig, k as ToolFirewallRule, l as TraceEvent } from './agent-
|
|
1
|
+
import { a as IProviderAdapter, d as TranscriptionRequest, e as TranscriptionResponse, f as SynthesisRequest, A as AudioChunk, V as VisionRequest, g as VisionResponse, h as ImageGenRequest, i as ImageGenResponse, M as ModelInfo, C as ContextWindow, T as Turn, j as ContentBlock, I as IToolDefinition } from './adapters-CIRkrCHl.js';
|
|
2
|
+
export { k as CompletionChunk, l as CompletionRequest, m as CompletionResponse, b as IContextStrategy, c as IScratchpadAdapter, n as IStorageAdapter, N as NormalizedMessage, o as STMItem, p as STMRegistryConfig, S as ShortTermMemoryRegistry, q as TokenUsage, r as ToolCall, s as ToolContext, t as ToolResult } from './adapters-CIRkrCHl.js';
|
|
3
|
+
import { S as SessionConfig, G as Goal, I as IToolResponseProcessor, T as ToolResponseEvaluation, M as MCPServerConfig, a as MCPToolDefinition } from './agent-Cq_oRvoc.js';
|
|
4
|
+
export { b as GoalInjector, c as GoalVerifierResult, d as MCPJsonRpcRequest, e as MCPJsonRpcResponse, f as MCPTransportType, g as MediaConfig, h as ToolDecision, i as ToolExecutionBudget, j as ToolFirewallConfig, k as ToolFirewallRule, l as TraceEvent } from './agent-Cq_oRvoc.js';
|
|
5
5
|
export { LemuraAdapterError, LemuraContextOverflowError, LemuraError, LemuraMCPConnectionError, LemuraMCPError, LemuraMCPTimeoutError, LemuraMaxIterationsError, LemuraSkillInjectionError, LemuraToolNotFoundError, LemuraToolTimeoutError, LemuraToolValidationError } from './types/index.js';
|
|
6
6
|
import { I as ILogger } from './logger-DxvKliuk.js';
|
|
7
7
|
export { L as LogLevel, a as LogMetadata, S as Severity } from './logger-DxvKliuk.js';
|
|
@@ -226,6 +226,8 @@ declare class SessionManager {
|
|
|
226
226
|
private toolResponseProcessor;
|
|
227
227
|
private goalInjector;
|
|
228
228
|
private continuationPlanner;
|
|
229
|
+
/** Frozen goal/plan injection text keyed by turn index — used when staticSystemPrompt is on */
|
|
230
|
+
private _turnInjections;
|
|
229
231
|
private mcpRegistry;
|
|
230
232
|
/** Resolves when all MCP servers are connected; awaited by run() and stream() */
|
|
231
233
|
private mcpReady;
|
package/dist/index.js
CHANGED
|
@@ -234,13 +234,27 @@ var OpenAICompatibleAdapter = class {
|
|
|
234
234
|
return msg;
|
|
235
235
|
});
|
|
236
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Returns true for reasoning/o-series models (o1, o3, o4, gpt-5, *-mini variants).
|
|
239
|
+
* These models use `max_completion_tokens` instead of `max_tokens` and do not
|
|
240
|
+
* support sampling hyperparameters (temperature, top_p, presence_penalty, frequency_penalty).
|
|
241
|
+
*/
|
|
242
|
+
isReasoningModel(model) {
|
|
243
|
+
return /\bo[1-9]\b|\bo[1-9]-|\bgpt-5\b|(?:^|[-_])mini(?:$|[-_])/i.test(model);
|
|
244
|
+
}
|
|
237
245
|
buildPayload(request) {
|
|
246
|
+
const model = request.model || this.defaultModel;
|
|
247
|
+
const reasoning = this.isReasoningModel(model);
|
|
238
248
|
const payload = {
|
|
239
|
-
model
|
|
249
|
+
model,
|
|
240
250
|
messages: this.toOpenAIMessages(request.messages)
|
|
241
251
|
};
|
|
242
|
-
if (request.maxTokens !== void 0)
|
|
243
|
-
|
|
252
|
+
if (request.maxTokens !== void 0) {
|
|
253
|
+
payload[reasoning ? "max_completion_tokens" : "max_tokens"] = request.maxTokens;
|
|
254
|
+
}
|
|
255
|
+
if (!reasoning) {
|
|
256
|
+
if (request.temperature !== void 0) payload.temperature = request.temperature;
|
|
257
|
+
}
|
|
244
258
|
if (request.stopSequences?.length) payload.stop = request.stopSequences;
|
|
245
259
|
if (request.stream) payload.stream = true;
|
|
246
260
|
if (request.tools && request.tools.length > 0) {
|
|
@@ -1039,8 +1053,8 @@ var ToolRegistry = class {
|
|
|
1039
1053
|
);
|
|
1040
1054
|
}
|
|
1041
1055
|
}
|
|
1042
|
-
const
|
|
1043
|
-
const
|
|
1056
|
+
const timeoutMs = tool.timeoutMs ?? this.defaultTimeoutMs;
|
|
1057
|
+
const startMs = Date.now();
|
|
1044
1058
|
const executionPromise = tool.execute(params, context);
|
|
1045
1059
|
const timeoutPromise = new Promise((_, reject) => {
|
|
1046
1060
|
const id = setTimeout(() => {
|
|
@@ -1051,12 +1065,26 @@ var ToolRegistry = class {
|
|
|
1051
1065
|
}, timeoutMs);
|
|
1052
1066
|
});
|
|
1053
1067
|
try {
|
|
1054
|
-
|
|
1068
|
+
const result = await Promise.race([executionPromise, timeoutPromise]);
|
|
1069
|
+
context.logger.debug(`Tool '${name}' completed in ${Date.now() - startMs}ms`);
|
|
1070
|
+
return result;
|
|
1055
1071
|
} catch (err) {
|
|
1056
|
-
|
|
1072
|
+
const elapsedMs = Date.now() - startMs;
|
|
1073
|
+
if (err instanceof LemuraToolTimeoutError) {
|
|
1074
|
+
context.logger.error(`Tool '${name}' timed out after ${elapsedMs}ms (limit: ${timeoutMs}ms)`, {
|
|
1075
|
+
problem: `Tool '${name}' did not respond within its timeout.`,
|
|
1076
|
+
hints: [
|
|
1077
|
+
`Increase the tool's timeoutMs (currently ${timeoutMs}ms) or optimise its implementation.`,
|
|
1078
|
+
`Check whether the external service the tool depends on is healthy.`
|
|
1079
|
+
]
|
|
1080
|
+
});
|
|
1081
|
+
throw err;
|
|
1082
|
+
}
|
|
1083
|
+
if (err instanceof LemuraToolValidationError) {
|
|
1057
1084
|
throw err;
|
|
1058
1085
|
}
|
|
1059
1086
|
const message = err instanceof Error ? err.message : String(err);
|
|
1087
|
+
context.logger.error(`Tool '${name}' failed after ${elapsedMs}ms: ${message}`);
|
|
1060
1088
|
throw new LemuraToolValidationError(
|
|
1061
1089
|
`Tool '${name}' execution failed: ${message}`
|
|
1062
1090
|
);
|
|
@@ -1287,8 +1315,9 @@ var SkillInjector = class {
|
|
|
1287
1315
|
if (!content) continue;
|
|
1288
1316
|
const tierLabel = skill.tier ?? "standard";
|
|
1289
1317
|
const skillEntry = `
|
|
1290
|
-
|
|
1318
|
+
<lemura:skill name="${skill.name}" tier="${tierLabel}">
|
|
1291
1319
|
${content}
|
|
1320
|
+
</lemura:skill>
|
|
1292
1321
|
`;
|
|
1293
1322
|
const skillTokens = Math.ceil(skillEntry.length / 4);
|
|
1294
1323
|
if (tokenBudget !== void 0 && usedTokens + skillTokens > tokenBudget) {
|
|
@@ -1825,7 +1854,7 @@ var GoalInjector = class {
|
|
|
1825
1854
|
};
|
|
1826
1855
|
}
|
|
1827
1856
|
/**
|
|
1828
|
-
* Returns the formatted
|
|
1857
|
+
* Returns the formatted `<lemura:goal>` block string — without caring about
|
|
1829
1858
|
* where it will be placed. Callers decide whether to append to a system prompt
|
|
1830
1859
|
* or push as a separate message.
|
|
1831
1860
|
*/
|
|
@@ -1833,27 +1862,28 @@ var GoalInjector = class {
|
|
|
1833
1862
|
const { statement, successCriteria, decomposition, completedSubGoals = [] } = this.goal;
|
|
1834
1863
|
const pending = decomposition.filter((sg) => !completedSubGoals.includes(sg));
|
|
1835
1864
|
const completed = decomposition.filter((sg) => completedSubGoals.includes(sg));
|
|
1836
|
-
let block =
|
|
1837
|
-
|
|
1865
|
+
let block = `<lemura:goal>
|
|
1866
|
+
<lemura:statement>${statement}</lemura:statement>
|
|
1838
1867
|
`;
|
|
1839
1868
|
if (successCriteria.length > 0) {
|
|
1840
|
-
block +=
|
|
1841
|
-
|
|
1842
|
-
|
|
1869
|
+
block += `<lemura:criteria>
|
|
1870
|
+
${successCriteria.map((c) => `- ${c}`).join("\n")}
|
|
1871
|
+
</lemura:criteria>
|
|
1872
|
+
`;
|
|
1843
1873
|
}
|
|
1844
1874
|
if (pending.length > 0) {
|
|
1845
|
-
block +=
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1875
|
+
block += `<lemura:subgoals status="pending">
|
|
1876
|
+
${pending.map((sg) => `- ${sg}`).join("\n")}
|
|
1877
|
+
</lemura:subgoals>
|
|
1878
|
+
`;
|
|
1849
1879
|
}
|
|
1850
1880
|
if (completed.length > 0) {
|
|
1851
|
-
block +=
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1881
|
+
block += `<lemura:subgoals status="done">
|
|
1882
|
+
${completed.map((sg) => `- \u2705 ${sg}`).join("\n")}
|
|
1883
|
+
</lemura:subgoals>
|
|
1884
|
+
`;
|
|
1855
1885
|
}
|
|
1856
|
-
block += "
|
|
1886
|
+
block += "</lemura:goal>";
|
|
1857
1887
|
return block;
|
|
1858
1888
|
}
|
|
1859
1889
|
/**
|
|
@@ -1942,14 +1972,17 @@ var ContinuationPlanner = class {
|
|
|
1942
1972
|
}
|
|
1943
1973
|
/** Returns a human-readable status string with icons (injected before each iteration) */
|
|
1944
1974
|
getPlanStatusString() {
|
|
1945
|
-
|
|
1975
|
+
const current = this.plan.currentStepIndex + 1;
|
|
1976
|
+
const total = this.plan.steps.length;
|
|
1977
|
+
let result = `<lemura:plan step="${current}" total="${total}">
|
|
1946
1978
|
`;
|
|
1947
1979
|
for (const step of this.plan.steps) {
|
|
1948
1980
|
const icon = this._icon(step.status);
|
|
1949
1981
|
const statusText = step.status === "pending" && step.dependsOn.length > 0 ? `Waiting on Step ${step.dependsOn.join(", ")}` : step.status.charAt(0).toUpperCase() + step.status.slice(1);
|
|
1950
|
-
result +=
|
|
1982
|
+
result += `<lemura:step id="${step.stepId}" tool="${step.toolName}" status="${step.status}">${icon} ${statusText}</lemura:step>
|
|
1951
1983
|
`;
|
|
1952
1984
|
}
|
|
1985
|
+
result += "</lemura:plan>";
|
|
1953
1986
|
return result;
|
|
1954
1987
|
}
|
|
1955
1988
|
_icon(status) {
|
|
@@ -2161,7 +2194,31 @@ var MCPClient = class {
|
|
|
2161
2194
|
method: "tools/call",
|
|
2162
2195
|
params: { name: toolName, arguments: args }
|
|
2163
2196
|
};
|
|
2164
|
-
const
|
|
2197
|
+
const startMs = Date.now();
|
|
2198
|
+
let response;
|
|
2199
|
+
try {
|
|
2200
|
+
response = await this._rpc(request);
|
|
2201
|
+
} catch (err) {
|
|
2202
|
+
const elapsedMs = Date.now() - startMs;
|
|
2203
|
+
if (err instanceof LemuraMCPTimeoutError) {
|
|
2204
|
+
this.logger.error(
|
|
2205
|
+
`[MCP:${this._serverName}] Tool '${toolName}' timed out after ${elapsedMs}ms (limit: ${this.timeoutMs}ms)`,
|
|
2206
|
+
{
|
|
2207
|
+
problem: `MCP server '${this._serverName}' did not respond to tool '${toolName}' in time.`,
|
|
2208
|
+
hints: [
|
|
2209
|
+
`Increase 'timeoutMs' in the MCPServerConfig for '${this._serverName}' (currently ${this.timeoutMs}ms).`,
|
|
2210
|
+
`Check whether the MCP server process is healthy and not blocked.`
|
|
2211
|
+
]
|
|
2212
|
+
}
|
|
2213
|
+
);
|
|
2214
|
+
} else {
|
|
2215
|
+
this.logger.error(
|
|
2216
|
+
`[MCP:${this._serverName}] Tool '${toolName}' RPC failed after ${elapsedMs}ms: ${err.message}`
|
|
2217
|
+
);
|
|
2218
|
+
}
|
|
2219
|
+
throw err;
|
|
2220
|
+
}
|
|
2221
|
+
this.logger.debug(`[MCP:${this._serverName}] Tool '${toolName}' completed in ${Date.now() - startMs}ms`);
|
|
2165
2222
|
this._assertNoError(response, `tool '${toolName}'`);
|
|
2166
2223
|
const result = response.result;
|
|
2167
2224
|
if (result && Array.isArray(result["content"])) {
|
|
@@ -2579,6 +2636,8 @@ var SessionManager = class {
|
|
|
2579
2636
|
toolResponseProcessor;
|
|
2580
2637
|
goalInjector = null;
|
|
2581
2638
|
continuationPlanner = null;
|
|
2639
|
+
/** Frozen goal/plan injection text keyed by turn index — used when staticSystemPrompt is on */
|
|
2640
|
+
_turnInjections = /* @__PURE__ */ new Map();
|
|
2582
2641
|
// MCP
|
|
2583
2642
|
mcpRegistry = null;
|
|
2584
2643
|
/** Resolves when all MCP servers are connected; awaited by run() and stream() */
|
|
@@ -2947,8 +3006,12 @@ Respond ONLY with valid JSON (no markdown, no explanations):
|
|
|
2947
3006
|
messages: [{ role: "user", content: planningPrompt }],
|
|
2948
3007
|
maxTokens: this.config.maxCompletionTokens ?? 4e3
|
|
2949
3008
|
});
|
|
2950
|
-
const
|
|
2951
|
-
const
|
|
3009
|
+
const stripped = response.content.replace(/```json|```/g, "").trim();
|
|
3010
|
+
const jsonMatch = stripped.match(/\{[\s\S]*\}/);
|
|
3011
|
+
if (!jsonMatch) {
|
|
3012
|
+
throw new Error(`No JSON object found in mini-planning response: "${stripped.slice(0, 200)}"`);
|
|
3013
|
+
}
|
|
3014
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
2952
3015
|
if (this.goalInjector && Array.isArray(parsed.subGoals)) {
|
|
2953
3016
|
this.goalInjector.updateDecomposition(
|
|
2954
3017
|
parsed.subGoals,
|
|
@@ -2971,7 +3034,8 @@ Respond ONLY with valid JSON (no markdown, no explanations):
|
|
|
2971
3034
|
/** Builds the system prompt, injecting skills and goal if configured. */
|
|
2972
3035
|
buildSystemPrompt(userMessage, iteration = 0) {
|
|
2973
3036
|
let prompt = this.context.systemPrompt || "";
|
|
2974
|
-
|
|
3037
|
+
const isStatic = this.config.staticSystemPrompt === true;
|
|
3038
|
+
if (!isStatic && this.goalInjector && this.config.goalInjectionPosition !== "pre_turn") {
|
|
2975
3039
|
const shouldInject = this.goalInjector.shouldInjectThisTurn(
|
|
2976
3040
|
iteration,
|
|
2977
3041
|
false,
|
|
@@ -2985,7 +3049,7 @@ Respond ONLY with valid JSON (no markdown, no explanations):
|
|
|
2985
3049
|
});
|
|
2986
3050
|
}
|
|
2987
3051
|
}
|
|
2988
|
-
if (this.continuationPlanner && this.config.enableContinuationPlanning) {
|
|
3052
|
+
if (!isStatic && this.continuationPlanner && this.config.enableContinuationPlanning) {
|
|
2989
3053
|
const planStatus = this.continuationPlanner.getPlanStatusString();
|
|
2990
3054
|
prompt += `
|
|
2991
3055
|
|
|
@@ -3011,7 +3075,7 @@ ${planStatus}`;
|
|
|
3011
3075
|
if (systemPrompt) {
|
|
3012
3076
|
messages.unshift({ role: "system", content: systemPrompt });
|
|
3013
3077
|
}
|
|
3014
|
-
if (this.goalInjector && this.config.goalInjectionPosition === "pre_turn") {
|
|
3078
|
+
if (!this.config.staticSystemPrompt && this.goalInjector && this.config.goalInjectionPosition === "pre_turn") {
|
|
3015
3079
|
const shouldInject = this.goalInjector.shouldInjectThisTurn(
|
|
3016
3080
|
iteration,
|
|
3017
3081
|
false,
|
|
@@ -3023,6 +3087,52 @@ ${planStatus}`;
|
|
|
3023
3087
|
this.emitTrace("planning", "goal_injected", { position: "pre_turn", iteration });
|
|
3024
3088
|
}
|
|
3025
3089
|
}
|
|
3090
|
+
if (this.config.staticSystemPrompt) {
|
|
3091
|
+
const totalTurns = this.context.turns.length;
|
|
3092
|
+
for (let i = 0; i < totalTurns; i++) {
|
|
3093
|
+
const msgIndex = i + 1;
|
|
3094
|
+
if (msgIndex >= messages.length) continue;
|
|
3095
|
+
const msg = messages[msgIndex];
|
|
3096
|
+
if (msg.role !== "user" && msg.role !== "tool") continue;
|
|
3097
|
+
let injectionBlock;
|
|
3098
|
+
if (i === totalTurns - 1) {
|
|
3099
|
+
const blocks = [];
|
|
3100
|
+
if (this.goalInjector) {
|
|
3101
|
+
const shouldInject = this.goalInjector.shouldInjectThisTurn(
|
|
3102
|
+
iteration,
|
|
3103
|
+
false,
|
|
3104
|
+
this.config.goalInjectionN ?? 3
|
|
3105
|
+
);
|
|
3106
|
+
if (shouldInject) blocks.push(this.goalInjector.getFormattedBlock());
|
|
3107
|
+
}
|
|
3108
|
+
if (this.continuationPlanner && this.config.enableContinuationPlanning) {
|
|
3109
|
+
blocks.push(this.continuationPlanner.getPlanStatusString());
|
|
3110
|
+
}
|
|
3111
|
+
injectionBlock = blocks.length > 0 ? `
|
|
3112
|
+
|
|
3113
|
+
<lemura:agent-state>
|
|
3114
|
+
${blocks.join("\n\n")}
|
|
3115
|
+
</lemura:agent-state>` : "";
|
|
3116
|
+
this._turnInjections.set(i, injectionBlock);
|
|
3117
|
+
if (injectionBlock) {
|
|
3118
|
+
this.emitTrace("planning", "goal_injected", { position: "frozen_turn", turnIndex: i, iteration });
|
|
3119
|
+
}
|
|
3120
|
+
} else {
|
|
3121
|
+
injectionBlock = this._turnInjections.get(i) ?? "";
|
|
3122
|
+
}
|
|
3123
|
+
if (!injectionBlock) continue;
|
|
3124
|
+
if (Array.isArray(msg.content)) {
|
|
3125
|
+
messages[msgIndex] = {
|
|
3126
|
+
...msg,
|
|
3127
|
+
content: msg.content.map(
|
|
3128
|
+
(item) => item.type === "text" ? { ...item, text: item.text + injectionBlock } : item
|
|
3129
|
+
)
|
|
3130
|
+
};
|
|
3131
|
+
} else {
|
|
3132
|
+
messages[msgIndex] = { ...msg, content: (msg.content ?? "") + injectionBlock };
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3026
3136
|
return messages;
|
|
3027
3137
|
}
|
|
3028
3138
|
/** Checks the tool execution budget and throws descriptively if exceeded. */
|
|
@@ -3477,6 +3587,10 @@ ${planStatus}`;
|
|
|
3477
3587
|
const verdict = await this._verifyGoal(this.context.turns);
|
|
3478
3588
|
if (verdict && !verdict.achieved && verdict.missing) {
|
|
3479
3589
|
this.logger.info(`[GoalVerifier] Incomplete \u2014 running silent correction: "${verdict.missing}"`);
|
|
3590
|
+
this.emitTrace("verification", "goal_correction_start", {
|
|
3591
|
+
missing: verdict.missing,
|
|
3592
|
+
reason: verdict.reason
|
|
3593
|
+
});
|
|
3480
3594
|
try {
|
|
3481
3595
|
const corrMsgs = this.buildMessages(this.buildSystemPrompt(verdict.missing, this.iterations), this.iterations);
|
|
3482
3596
|
corrMsgs.push({ role: "user", content: verdict.missing });
|
|
@@ -3493,9 +3607,37 @@ ${planStatus}`;
|
|
|
3493
3607
|
turnIndex: this.context.turns.length,
|
|
3494
3608
|
compressed: false
|
|
3495
3609
|
});
|
|
3610
|
+
this.emitTrace("verification", "goal_correction_done", {
|
|
3611
|
+
missing: verdict.missing,
|
|
3612
|
+
correctionTokens: correction.usage?.completionTokens
|
|
3613
|
+
});
|
|
3496
3614
|
}
|
|
3497
3615
|
} catch (err) {
|
|
3498
|
-
|
|
3616
|
+
const errMsg = err.message;
|
|
3617
|
+
this.logger.warn(`[GoalVerifier] Correction failed (non-fatal): ${errMsg}`);
|
|
3618
|
+
this.emitTrace("error", "goal_correction_failed", { error: errMsg }, null, null, "error");
|
|
3619
|
+
}
|
|
3620
|
+
const finalVerdict = await this._verifyGoal(this.context.turns);
|
|
3621
|
+
if (finalVerdict && !finalVerdict.achieved) {
|
|
3622
|
+
this.logger.warn(`[GoalVerifier] Final verification failed: ${finalVerdict.reason}`);
|
|
3623
|
+
this.emitTrace("verification", "goal_verification_result", {
|
|
3624
|
+
achieved: false,
|
|
3625
|
+
reason: finalVerdict.reason,
|
|
3626
|
+
missing: finalVerdict.missing
|
|
3627
|
+
}, null, null, "error");
|
|
3628
|
+
const warningBlock = `
|
|
3629
|
+
|
|
3630
|
+
---
|
|
3631
|
+
|
|
3632
|
+
\u26A0\uFE0F **Goal Verification Warning**
|
|
3633
|
+
* **Status:** Success criteria not fully met.
|
|
3634
|
+
* **Reason:** ${finalVerdict.reason ?? "Unknown"}
|
|
3635
|
+
* **Missing:** ${finalVerdict.missing ?? "Not specified"}
|
|
3636
|
+
|
|
3637
|
+
`;
|
|
3638
|
+
yield warningBlock;
|
|
3639
|
+
const lastTurn = [...this.context.turns].reverse().find((t) => t.role === "assistant");
|
|
3640
|
+
if (lastTurn) lastTurn.content = lastTurn.content + warningBlock;
|
|
3499
3641
|
}
|
|
3500
3642
|
}
|
|
3501
3643
|
}
|
|
@@ -3624,6 +3766,12 @@ ${planStatus}`;
|
|
|
3624
3766
|
const msg = e instanceof Error ? e.message : String(e);
|
|
3625
3767
|
const isTimeout = e instanceof LemuraToolTimeoutError;
|
|
3626
3768
|
this.logger.error(`Tool ${tc.name} ${isTimeout ? "timed out" : "failed"}: ${msg}`);
|
|
3769
|
+
this.emitTrace("error", isTimeout ? "tool_timeout" : "tool_error", {
|
|
3770
|
+
toolName: tc.name,
|
|
3771
|
+
id: tc.id,
|
|
3772
|
+
error: msg,
|
|
3773
|
+
timeoutMs: isTimeout ? this.config.toolRegistryTimeoutMs ?? 3e4 : void 0
|
|
3774
|
+
}, null, null, "error");
|
|
3627
3775
|
return { toolCallId: tc.id, content: `Error: ${msg}` };
|
|
3628
3776
|
}
|
|
3629
3777
|
})
|
|
@@ -3644,6 +3792,12 @@ ${planStatus}`;
|
|
|
3644
3792
|
problem: `Tool ${tc.name} ${isTimeout ? "timed out" : "failed to execute"}.`,
|
|
3645
3793
|
hints: isTimeout ? ["Increase toolRegistryTimeoutMs or optimise the tool implementation."] : ["Check the tool parameters and ensure required services are running."]
|
|
3646
3794
|
});
|
|
3795
|
+
this.emitTrace("error", isTimeout ? "tool_timeout" : "tool_error", {
|
|
3796
|
+
toolName: tc.name,
|
|
3797
|
+
id: tc.id,
|
|
3798
|
+
error: msg,
|
|
3799
|
+
timeoutMs: isTimeout ? this.config.toolRegistryTimeoutMs ?? 3e4 : void 0
|
|
3800
|
+
}, null, null, "error");
|
|
3647
3801
|
toolResults.push({ toolCallId: tc.id, content: `Error: ${msg}` });
|
|
3648
3802
|
}
|
|
3649
3803
|
}
|
|
@@ -3688,6 +3842,10 @@ ${planStatus}`;
|
|
|
3688
3842
|
const verdict = await this._verifyGoal(this.context.turns);
|
|
3689
3843
|
if (verdict && !verdict.achieved && verdict.missing) {
|
|
3690
3844
|
this.logger.info(`[GoalVerifier] Incomplete \u2014 running silent correction: "${verdict.missing}"`);
|
|
3845
|
+
this.emitTrace("verification", "goal_correction_start", {
|
|
3846
|
+
missing: verdict.missing,
|
|
3847
|
+
reason: verdict.reason
|
|
3848
|
+
});
|
|
3691
3849
|
try {
|
|
3692
3850
|
const correctionMessages = this.buildMessages(
|
|
3693
3851
|
this.buildSystemPrompt(verdict.missing, this.iterations),
|
|
@@ -3707,11 +3865,40 @@ ${planStatus}`;
|
|
|
3707
3865
|
turnIndex: this.context.turns.length,
|
|
3708
3866
|
compressed: false
|
|
3709
3867
|
});
|
|
3868
|
+
this.emitTrace("verification", "goal_correction_done", {
|
|
3869
|
+
missing: verdict.missing,
|
|
3870
|
+
correctionTokens: correction.usage?.completionTokens
|
|
3871
|
+
});
|
|
3710
3872
|
}
|
|
3711
3873
|
} catch (err) {
|
|
3712
|
-
|
|
3874
|
+
const errMsg = err.message;
|
|
3875
|
+
this.logger.warn(`[GoalVerifier] Correction failed (non-fatal): ${errMsg}`);
|
|
3876
|
+
this.emitTrace("error", "goal_correction_failed", { error: errMsg }, null, null, "error");
|
|
3713
3877
|
}
|
|
3714
3878
|
}
|
|
3879
|
+
const finalVerdict = await this._verifyGoal(this.context.turns);
|
|
3880
|
+
if (finalVerdict && !finalVerdict.achieved) {
|
|
3881
|
+
this.logger.warn(`[GoalVerifier] Final verification failed: ${finalVerdict.reason}`);
|
|
3882
|
+
this.emitTrace("verification", "goal_verification_result", {
|
|
3883
|
+
achieved: false,
|
|
3884
|
+
reason: finalVerdict.reason,
|
|
3885
|
+
missing: finalVerdict.missing
|
|
3886
|
+
}, null, null, "error");
|
|
3887
|
+
const warningBlock = `
|
|
3888
|
+
|
|
3889
|
+
---
|
|
3890
|
+
|
|
3891
|
+
\u26A0\uFE0F **Goal Verification Warning**
|
|
3892
|
+
* **Status:** Success criteria not fully met.
|
|
3893
|
+
* **Reason:** ${finalVerdict.reason ?? "Unknown"}
|
|
3894
|
+
* **Missing:** ${finalVerdict.missing ?? "Not specified"}
|
|
3895
|
+
|
|
3896
|
+
`;
|
|
3897
|
+
const lastTurn = [...this.context.turns].reverse().find((t) => t.role === "assistant");
|
|
3898
|
+
if (lastTurn) lastTurn.content = lastTurn.content + warningBlock;
|
|
3899
|
+
this.logger.info(`[${opts.label}] Run completed with goal warning`);
|
|
3900
|
+
return lastTurn?.content ?? response.content;
|
|
3901
|
+
}
|
|
3715
3902
|
}
|
|
3716
3903
|
this.logger.info(`[${opts.label}] Run completed successfully`);
|
|
3717
3904
|
return response.content;
|