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.
Files changed (48) hide show
  1. package/dist/agent/agent.js +1 -1
  2. package/dist/agent/agent.js.map +1 -1
  3. package/dist/agent/agentLoop.js +77 -12
  4. package/dist/agent/agentLoop.js.map +1 -1
  5. package/dist/agent/agentWakeup.js +8 -3
  6. package/dist/agent/agentWakeup.js.map +1 -1
  7. package/dist/agent/commandPost.js +6 -1
  8. package/dist/agent/commandPost.js.map +1 -1
  9. package/dist/agent/heartbeatScheduler.js +36 -4
  10. package/dist/agent/heartbeatScheduler.js.map +1 -1
  11. package/dist/agent/toolRunner.js +30 -0
  12. package/dist/agent/toolRunner.js.map +1 -1
  13. package/dist/config/config.js +30 -8
  14. package/dist/config/config.js.map +1 -1
  15. package/dist/config/schema.js +10 -1
  16. package/dist/config/schema.js.map +1 -1
  17. package/dist/eval/record.js +1 -1
  18. package/dist/eval/record.js.map +1 -1
  19. package/dist/gateway/server.js +26 -0
  20. package/dist/gateway/server.js.map +1 -1
  21. package/dist/mesh/transport.js +60 -8
  22. package/dist/mesh/transport.js.map +1 -1
  23. package/dist/providers/anthropic.js +3 -2
  24. package/dist/providers/anthropic.js.map +1 -1
  25. package/dist/providers/base.js.map +1 -1
  26. package/dist/providers/google.js +94 -20
  27. package/dist/providers/google.js.map +1 -1
  28. package/dist/providers/modelCapabilities.js +59 -0
  29. package/dist/providers/modelCapabilities.js.map +1 -0
  30. package/dist/providers/ollama.js +3 -2
  31. package/dist/providers/ollama.js.map +1 -1
  32. package/dist/providers/openai.js +4 -3
  33. package/dist/providers/openai.js.map +1 -1
  34. package/dist/providers/openai_compat.js +3 -2
  35. package/dist/providers/openai_compat.js.map +1 -1
  36. package/dist/providers/router.js +63 -21
  37. package/dist/providers/router.js.map +1 -1
  38. package/dist/skills/registry.js +176 -163
  39. package/dist/skills/registry.js.map +1 -1
  40. package/dist/telemetry/activityLog.js +1 -1
  41. package/dist/telemetry/activityLog.js.map +1 -1
  42. package/dist/utils/constants.js +2 -2
  43. package/dist/utils/constants.js.map +1 -1
  44. package/docs/AGENT-HIERARCHY.md +154 -0
  45. package/docs/superpowers/plans/2026-04-29-titan-production-fix.md +241 -0
  46. package/package.json +2 -2
  47. package/scripts/start-workers.sh +39 -0
  48. package/scripts/task-feeder.ts +38 -0
@@ -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 || 12e3;
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
- if (ctx.isAutonomous && round < ctx.effectiveMaxRounds - 2) {
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 discoveryTools = ["gallery_search", "tool_search"];
1346
- const lastWasDiscovery = pendingToolCalls.length === 1 && discoveryTools.includes(pendingToolCalls[0].function.name);
1347
- const resultHintsFollowUp = lastWasDiscovery && toolResults.some(
1348
- (r) => /call gallery_get/i.test(r.content) || /hint:.*?(call|use|try|fetch|get|search)/i.test(r.content)
1349
- );
1350
- if (resultHintsFollowUp && round < ctx.effectiveMaxRounds - 1) {
1351
- logger.info(COMPONENT, `[DiscoveryChain] ${pendingToolCalls[0].function.name} hinted follow-up \u2014 allowing another think round`);
1352
- phase = "think";
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
- phase = "respond";
1355
- logger.info(COMPONENT, `[ThinkAct] Tools executed \u2014 entering respond phase (tools will be stripped)`);
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 = {