titan-agent 5.4.0 → 5.4.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/dist/agent/agent.js +1 -1
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/agentLoop.js +77 -12
- package/dist/agent/agentLoop.js.map +1 -1
- package/dist/agent/agentWakeup.js +8 -3
- package/dist/agent/agentWakeup.js.map +1 -1
- package/dist/agent/commandPost.js +6 -1
- package/dist/agent/commandPost.js.map +1 -1
- package/dist/agent/heartbeatScheduler.js +36 -4
- package/dist/agent/heartbeatScheduler.js.map +1 -1
- package/dist/agent/toolRunner.js +30 -0
- package/dist/agent/toolRunner.js.map +1 -1
- package/dist/config/config.js +30 -8
- package/dist/config/config.js.map +1 -1
- package/dist/config/schema.js +10 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/eval/record.js +1 -1
- package/dist/eval/record.js.map +1 -1
- package/dist/gateway/server.js +26 -0
- package/dist/gateway/server.js.map +1 -1
- package/dist/mesh/transport.js +60 -8
- package/dist/mesh/transport.js.map +1 -1
- package/dist/providers/anthropic.js +3 -2
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/base.js.map +1 -1
- package/dist/providers/google.js +94 -20
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/modelCapabilities.js +59 -0
- package/dist/providers/modelCapabilities.js.map +1 -0
- package/dist/providers/ollama.js +3 -2
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.js +4 -3
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openai_compat.js +3 -2
- package/dist/providers/openai_compat.js.map +1 -1
- package/dist/providers/router.js +63 -21
- package/dist/providers/router.js.map +1 -1
- package/dist/skills/registry.js +176 -163
- package/dist/skills/registry.js.map +1 -1
- package/dist/telemetry/activityLog.js +1 -1
- package/dist/telemetry/activityLog.js.map +1 -1
- package/dist/utils/constants.js +2 -2
- package/dist/utils/constants.js.map +1 -1
- package/docs/AGENT-HIERARCHY.md +154 -0
- package/docs/superpowers/plans/2026-04-29-titan-production-fix.md +241 -0
- package/package.json +2 -2
- package/scripts/start-workers.sh +39 -0
- package/scripts/task-feeder.ts +38 -0
package/dist/agent/agentLoop.js
CHANGED
|
@@ -26,6 +26,7 @@ async function chatWithTimeout(fn, label, timeoutMs = 3e5) {
|
|
|
26
26
|
import { buildSmartContext } from "./contextManager.js";
|
|
27
27
|
import { isKilled } from "../safety/killSwitch.js";
|
|
28
28
|
import { estimateTokens } from "../utils/tokens.js";
|
|
29
|
+
import { DEFAULT_MAX_TOKENS } from "../utils/constants.js";
|
|
29
30
|
import { getCachedResponse, setCachedResponse } from "./responseCache.js";
|
|
30
31
|
import { shouldReflect, reflect, resetProgress, recordProgress, setProgressSession } from "./reflection.js";
|
|
31
32
|
import { recordToolResult, classifyTaskType, recordToolPreference, getErrorResolution, recordErrorResolution } from "../memory/learning.js";
|
|
@@ -442,7 +443,7 @@ async function runAgentLoop(ctx) {
|
|
|
442
443
|
let budgetWarningSent = false;
|
|
443
444
|
let pendingToolCalls = [];
|
|
444
445
|
let pendingAssistantContent = "";
|
|
445
|
-
const tokenBudget = ctx.voiceFastPath ? 2e3 : ctx.config.agent.tokenBudget ||
|
|
446
|
+
const tokenBudget = ctx.voiceFastPath ? 2e3 : ctx.config.agent.tokenBudget || DEFAULT_MAX_TOKENS;
|
|
446
447
|
globalSteerQueues.set(ctx.sessionId, ctx.steerQueue ?? []);
|
|
447
448
|
const traceCtx = newTraceContext();
|
|
448
449
|
initOtel();
|
|
@@ -663,6 +664,23 @@ Try a COMPLETELY DIFFERENT strategy. Do NOT repeat the same tools or approach.`
|
|
|
663
664
|
ctx.streamCallbacks.onToolCall?.(chunk.toolCall.function.name, JSON.parse(chunk.toolCall.function.arguments || "{}"));
|
|
664
665
|
} else if (chunk.type === "error") {
|
|
665
666
|
logger.error(COMPONENT, `Stream error: ${chunk.error}`);
|
|
667
|
+
} else if (chunk.type === "retry") {
|
|
668
|
+
logger.warn(COMPONENT, `Stream retrying ${chunk.provider}/${chunk.model} (attempt ${chunk.attempt}/${chunk.maxRetries}, reason=${chunk.reason}, delay=${chunk.delayMs}ms)`);
|
|
669
|
+
ctx.streamCallbacks.onRetry?.({
|
|
670
|
+
attempt: chunk.attempt,
|
|
671
|
+
maxRetries: chunk.maxRetries,
|
|
672
|
+
reason: chunk.reason,
|
|
673
|
+
provider: chunk.provider,
|
|
674
|
+
model: chunk.model,
|
|
675
|
+
delayMs: chunk.delayMs
|
|
676
|
+
});
|
|
677
|
+
} else if (chunk.type === "failover") {
|
|
678
|
+
logger.warn(COMPONENT, `Stream failover from ${chunk.originalProvider}/${chunk.originalModel}${chunk.error ? ` (${chunk.error})` : ""}`);
|
|
679
|
+
ctx.streamCallbacks.onFailover?.({
|
|
680
|
+
originalProvider: chunk.originalProvider,
|
|
681
|
+
originalModel: chunk.originalModel,
|
|
682
|
+
error: chunk.error
|
|
683
|
+
});
|
|
666
684
|
}
|
|
667
685
|
}
|
|
668
686
|
const estCompletionTokens = estimateTokens(streamContent + JSON.stringify(streamToolCalls));
|
|
@@ -929,7 +947,11 @@ Execute the next ACTION.` });
|
|
|
929
947
|
phase = "done";
|
|
930
948
|
break;
|
|
931
949
|
}
|
|
932
|
-
|
|
950
|
+
const isMultiStepPipeline = ctx.completionStrategy === "terminal-tool" || ctx.minRounds !== void 0;
|
|
951
|
+
const nudgeMinRounds = ctx.minRounds ?? (ctx.completionStrategy === "single-round" ? 1 : 2);
|
|
952
|
+
const nudgeMinRoundsMet = round >= nudgeMinRounds;
|
|
953
|
+
const nudgeEnabled = ctx.isAutonomous || isMultiStepPipeline && !nudgeMinRoundsMet;
|
|
954
|
+
if (nudgeEnabled && round < ctx.effectiveMaxRounds - 2) {
|
|
933
955
|
const futureIntentOpener = /^(let me\s+\w+|I['']?ll\s+(?:start|begin|check|look|read|run|edit|write|create|try|go|investigate|verify|test|install|build|fix|update|change|set)|I\s+(?:will|need to|should|can|am going to|plan to)\s+\w+|first,?\s+I|now\s+I|to\s+(?:fix|resolve|complete|edit|write|create|update|change|run))\b/i.test(response.content.trim());
|
|
934
956
|
const describesWork = /\b(?:I['']?ll|I (?:will|need to|should|plan to|am going to)|let me)\b[^.]{0,80}\b(?:fix|edit|change|update|create|write|modify|run|install|build|start|restart|read|open|debug|set up|check|look at|examine|investigate|verify|confirm|test)\b/i.test(response.content);
|
|
935
957
|
if (futureIntentOpener && describesWork && noToolsRetryCount < 3) {
|
|
@@ -1056,6 +1078,17 @@ ${gateText}` : gateText;
|
|
|
1056
1078
|
tr.diff
|
|
1057
1079
|
);
|
|
1058
1080
|
}
|
|
1081
|
+
const pendingApproval = toolResults.find((r) => r.approvalPending);
|
|
1082
|
+
if (pendingApproval) {
|
|
1083
|
+
logger.info(COMPONENT, `[ApprovalPause] Tool "${pendingApproval.name}" awaiting approval (req ${pendingApproval.approvalRequestId}) \u2014 exiting loop`);
|
|
1084
|
+
result.content = pendingApproval.content;
|
|
1085
|
+
ctx.messages.push({
|
|
1086
|
+
role: "assistant",
|
|
1087
|
+
content: `I need your approval before I can use ${pendingApproval.name}. ${pendingApproval.content}`
|
|
1088
|
+
});
|
|
1089
|
+
phase = "done";
|
|
1090
|
+
break;
|
|
1091
|
+
}
|
|
1059
1092
|
try {
|
|
1060
1093
|
const { getSubdirTracker } = await import("./subdirHints.js");
|
|
1061
1094
|
const tracker = getSubdirTracker(ctx.sessionId);
|
|
@@ -1342,17 +1375,32 @@ Examples: use read_file instead of cat/head/tail/grep, and edit_file instead of
|
|
|
1342
1375
|
}
|
|
1343
1376
|
}
|
|
1344
1377
|
} else {
|
|
1345
|
-
const
|
|
1346
|
-
const
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1378
|
+
const hasPipelineConfig = ctx.completionStrategy !== void 0 || ctx.minRounds !== void 0;
|
|
1379
|
+
const isMultiStep = hasPipelineConfig && (ctx.completionStrategy === "terminal-tool" || ctx.minRounds !== void 0);
|
|
1380
|
+
if (isMultiStep) {
|
|
1381
|
+
const defaultTerminals = ["write_file", "append_file", "fb_post", "fb_reply", "content_publish"];
|
|
1382
|
+
const terminalTools = new Set(ctx.pipelineTerminalTools || defaultTerminals);
|
|
1383
|
+
const terminalMissing = !toolResults.some((r) => terminalTools.has(r.name));
|
|
1384
|
+
if (terminalMissing && round < ctx.effectiveMaxRounds - 1) {
|
|
1385
|
+
logger.info(COMPONENT, `[NonAutonomous] Multi-step task needs terminal tool \u2014 staying in THINK`);
|
|
1386
|
+
ctx.messages.push({ role: "user", content: "Continue with the task. Call the required tool to finish." });
|
|
1387
|
+
phase = "think";
|
|
1388
|
+
} else {
|
|
1389
|
+
phase = "respond";
|
|
1390
|
+
}
|
|
1353
1391
|
} else {
|
|
1354
|
-
|
|
1355
|
-
|
|
1392
|
+
const discoveryTools = ["gallery_search", "tool_search"];
|
|
1393
|
+
const lastWasDiscovery = pendingToolCalls.length === 1 && discoveryTools.includes(pendingToolCalls[0].function.name);
|
|
1394
|
+
const resultHintsFollowUp = lastWasDiscovery && toolResults.some(
|
|
1395
|
+
(r) => /call gallery_get/i.test(r.content) || /hint:.*?(call|use|try|fetch|get|search)/i.test(r.content)
|
|
1396
|
+
);
|
|
1397
|
+
if (resultHintsFollowUp && round < ctx.effectiveMaxRounds - 1) {
|
|
1398
|
+
logger.info(COMPONENT, `[DiscoveryChain] ${pendingToolCalls[0].function.name} hinted follow-up \u2014 allowing another think round`);
|
|
1399
|
+
phase = "think";
|
|
1400
|
+
} else {
|
|
1401
|
+
phase = "respond";
|
|
1402
|
+
logger.info(COMPONENT, `[ThinkAct] Tools executed \u2014 entering respond phase (tools will be stripped)`);
|
|
1403
|
+
}
|
|
1356
1404
|
}
|
|
1357
1405
|
}
|
|
1358
1406
|
break;
|
|
@@ -1423,6 +1471,23 @@ Examples: use read_file instead of cat/head/tail/grep, and edit_file instead of
|
|
|
1423
1471
|
ctx.streamCallbacks.onToken(chunk.content);
|
|
1424
1472
|
} else if (chunk.type === "error") {
|
|
1425
1473
|
logger.error(COMPONENT, `Stream error: ${chunk.error}`);
|
|
1474
|
+
} else if (chunk.type === "retry") {
|
|
1475
|
+
logger.warn(COMPONENT, `Respond-phase stream retrying ${chunk.provider}/${chunk.model} (attempt ${chunk.attempt}/${chunk.maxRetries}, reason=${chunk.reason})`);
|
|
1476
|
+
ctx.streamCallbacks.onRetry?.({
|
|
1477
|
+
attempt: chunk.attempt,
|
|
1478
|
+
maxRetries: chunk.maxRetries,
|
|
1479
|
+
reason: chunk.reason,
|
|
1480
|
+
provider: chunk.provider,
|
|
1481
|
+
model: chunk.model,
|
|
1482
|
+
delayMs: chunk.delayMs
|
|
1483
|
+
});
|
|
1484
|
+
} else if (chunk.type === "failover") {
|
|
1485
|
+
logger.warn(COMPONENT, `Respond-phase stream failover from ${chunk.originalProvider}/${chunk.originalModel}`);
|
|
1486
|
+
ctx.streamCallbacks.onFailover?.({
|
|
1487
|
+
originalProvider: chunk.originalProvider,
|
|
1488
|
+
originalModel: chunk.originalModel,
|
|
1489
|
+
error: chunk.error
|
|
1490
|
+
});
|
|
1426
1491
|
}
|
|
1427
1492
|
}
|
|
1428
1493
|
response = {
|