{"version":3,"file":"no_model_in_tool_handler.mjs","names":[],"sources":["../../../src/eslint/rules/no_model_in_tool_handler.ts"],"sourcesContent":["/**\n * @module @nhtio/adk/eslint/rules/no_model_in_tool_handler\n *\n * Flags an LLM/model call inside a `Tool` / `ArtifactTool` `handler`.\n *\n * Why: the executor owns the dispatch loop. A tool handler runs as one step the model proposed; it\n * must do work and return a result, not start its own reasoning loop. Calling a model from inside a\n * handler hides a nested, unmanaged dispatch from the harness — no token accounting, no event\n * surfacing, no abort propagation. The one legitimate exception is a tool that is explicitly a\n * sub-agent running its own scoped dispatch; this rule allows a handler that constructs a\n * `new TurnRunner(...)` or calls the lower-level `DispatchRunner.dispatch(...)`.\n *\n * Detects provider SDK constructors (`new OpenAI()`, `new Anthropic()`) and chat/completion calls\n * (a `.create()` whose receiver chain passes through `messages`/`completions`/`responses`, or a\n * `generateContent()` call) within the `handler` function passed to `new Tool({ handler })` /\n * `new ArtifactTool({ handler })`, ignoring inner closures (so a sub-agent's own callbacks don't\n * false-positive).\n *\n * Opt-out:\n *   // eslint-disable-next-line adk/no-model-in-tool-handler -- <reason>\n */\n\nimport {\n  createRule,\n  isFunctionNode,\n  isLlmCall,\n  runsSubAgent,\n  walkBodySkippingNestedFunctions,\n} from './common'\n\nimport type { TSESTree } from '@typescript-eslint/utils'\n\nconst literalKey = (prop: TSESTree.Property): string | undefined => {\n  if (prop.computed) return undefined\n  if (prop.key.type === 'Identifier') return prop.key.name\n  if (prop.key.type === 'Literal' && typeof prop.key.value === 'string') return prop.key.value\n  return undefined\n}\n\n/** ESLint rule: flags referencing the model or LLM adapter inside a tool handler. */\nconst noModelInToolHandlerRule = createRule({\n  name: 'no-model-in-tool-handler',\n  meta: {\n    type: 'problem',\n    docs: {\n      description:\n        'A Tool/ArtifactTool handler must not invoke a model — the executor owns the dispatch loop. The only exception is a sub-agent tool that runs its own scoped dispatch via `new TurnRunner(...)` or `DispatchRunner.dispatch(...)`.',\n    },\n    schema: [],\n    messages: {\n      noModelInHandler:\n        'Do not call a model inside a tool handler — the executor owns the dispatch loop, and a model call here hides an unmanaged nested dispatch (no token accounting, events, or abort). If this tool is a sub-agent, run a scoped `new TurnRunner(...)` or `DispatchRunner.dispatch(...)` instead. Opt out with an eslint-disable-next-line adk/no-model-in-tool-handler comment + reason.',\n    },\n  },\n  defaultOptions: [],\n  create(context) {\n    return {\n      NewExpression(node: TSESTree.NewExpression) {\n        if (\n          node.callee.type !== 'Identifier' ||\n          (node.callee.name !== 'Tool' && node.callee.name !== 'ArtifactTool')\n        ) {\n          return\n        }\n        const arg = node.arguments[0]\n        if (!arg || arg.type !== 'ObjectExpression') return\n\n        const handlerProp = arg.properties.find(\n          (p): p is TSESTree.Property => p.type === 'Property' && literalKey(p) === 'handler'\n        )\n        if (!handlerProp || !isFunctionNode(handlerProp.value)) return\n        const handler = handlerProp.value\n\n        // A sub-agent tool legitimately runs a scoped dispatch (new TurnRunner / DispatchRunner.dispatch)\n        // — that's the documented exception.\n        if (runsSubAgent(handler.body)) return\n\n        walkBodySkippingNestedFunctions(handler.body, (n) => {\n          if (isLlmCall(n)) {\n            context.report({ node: n, messageId: 'noModelInHandler' })\n          }\n        })\n      },\n    }\n  },\n})\n\nexport default noModelInToolHandlerRule\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAM,cAAc,SAAgD;CAClE,IAAI,KAAK,UAAU,OAAO,KAAA;CAC1B,IAAI,KAAK,IAAI,SAAS,cAAc,OAAO,KAAK,IAAI;CACpD,IAAI,KAAK,IAAI,SAAS,aAAa,OAAO,KAAK,IAAI,UAAU,UAAU,OAAO,KAAK,IAAI;AAEzF;;AAGA,IAAM,2BAA2B,WAAW;CAC1C,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,mOACJ;EACA,QAAQ,CAAC;EACT,UAAU,EACR,kBACE,wXACJ;CACF;CACA,gBAAgB,CAAC;CACjB,OAAO,SAAS;EACd,OAAO,EACL,cAAc,MAA8B;GAC1C,IACE,KAAK,OAAO,SAAS,gBACpB,KAAK,OAAO,SAAS,UAAU,KAAK,OAAO,SAAS,gBAErD;GAEF,MAAM,MAAM,KAAK,UAAU;GAC3B,IAAI,CAAC,OAAO,IAAI,SAAS,oBAAoB;GAE7C,MAAM,cAAc,IAAI,WAAW,MAChC,MAA8B,EAAE,SAAS,cAAc,WAAW,CAAC,MAAM,SAC5E;GACA,IAAI,CAAC,eAAe,CAAC,eAAe,YAAY,KAAK,GAAG;GACxD,MAAM,UAAU,YAAY;GAI5B,IAAI,aAAa,QAAQ,IAAI,GAAG;GAEhC,gCAAgC,QAAQ,OAAO,MAAM;IACnD,IAAI,UAAU,CAAC,GACb,QAAQ,OAAO;KAAE,MAAM;KAAG,WAAW;IAAmB,CAAC;GAE7D,CAAC;EACH,EACF;CACF;AACF,CAAC"}