@nhtio/adk 1.20260607.0 → 1.20260607.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 +29 -0
- package/common-BUIjZ6LV.mjs +119 -0
- package/common-BUIjZ6LV.mjs.map +1 -0
- package/common-DeNipTQI.js +148 -0
- package/common-DeNipTQI.js.map +1 -0
- package/eslint/index.d.ts +106 -0
- package/eslint/rules/artifact_tool_forbids_artifact_constructor.cjs +57 -0
- package/eslint/rules/artifact_tool_forbids_artifact_constructor.cjs.map +1 -0
- package/eslint/rules/artifact_tool_forbids_artifact_constructor.d.ts +21 -0
- package/eslint/rules/artifact_tool_forbids_artifact_constructor.mjs +52 -0
- package/eslint/rules/artifact_tool_forbids_artifact_constructor.mjs.map +1 -0
- package/eslint/rules/common.d.ts +41 -0
- package/eslint/rules/index.d.ts +12 -0
- package/eslint/rules/no_model_in_tool_handler.cjs +64 -0
- package/eslint/rules/no_model_in_tool_handler.cjs.map +1 -0
- package/eslint/rules/no_model_in_tool_handler.d.ts +26 -0
- package/eslint/rules/no_model_in_tool_handler.mjs +59 -0
- package/eslint/rules/no_model_in_tool_handler.mjs.map +1 -0
- package/eslint/rules/require_validator_any_required.cjs +100 -0
- package/eslint/rules/require_validator_any_required.cjs.map +1 -0
- package/eslint/rules/require_validator_any_required.d.ts +31 -0
- package/eslint/rules/require_validator_any_required.mjs +95 -0
- package/eslint/rules/require_validator_any_required.mjs.map +1 -0
- package/eslint/rules/thought_payload_requires_replay_tag.cjs +71 -0
- package/eslint/rules/thought_payload_requires_replay_tag.cjs.map +1 -0
- package/eslint/rules/thought_payload_requires_replay_tag.d.ts +26 -0
- package/eslint/rules/thought_payload_requires_replay_tag.mjs +66 -0
- package/eslint/rules/thought_payload_requires_replay_tag.mjs.map +1 -0
- package/eslint/rules/token_encoding_requires_context_window.cjs +70 -0
- package/eslint/rules/token_encoding_requires_context_window.cjs.map +1 -0
- package/eslint/rules/token_encoding_requires_context_window.d.ts +24 -0
- package/eslint/rules/token_encoding_requires_context_window.mjs +65 -0
- package/eslint/rules/token_encoding_requires_context_window.mjs.map +1 -0
- package/eslint/rules.cjs +12 -0
- package/eslint/rules.mjs +6 -0
- package/eslint.cjs +82 -0
- package/eslint.cjs.map +1 -0
- package/eslint.mjs +74 -0
- package/eslint.mjs.map +1 -0
- package/index.cjs +1 -1
- package/index.mjs +1 -1
- package/mcp/adk-docs-corpus.json +1 -1
- package/package.json +230 -187
- package/skills/adk-assembly/SKILL.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
+
- **SoDK — a mental-model doc that teaches the loop in human terms.** A new page,
|
|
13
|
+
`docs/sodk.md` ("Society Development Kit"), retells [How agents work](https://adk.nht.io/how-agents-work)
|
|
14
|
+
and [What ADK is](https://adk.nht.io/what-adk-is) with exactly one noun swapped: where ADK says
|
|
15
|
+
*model*, SoDK says *person*. It is a teaching device for the reader who can't yet see why an agent
|
|
16
|
+
is the loop, not the LLM — role↔agent, task↔turn, briefing↔context, request↔tool, process↔middleware,
|
|
17
|
+
"say where things get filed"↔the required storage callbacks. The human-facing prose plays it
|
|
18
|
+
straight; the `<llm-only>` block names the metaphor outright and carries the full 1:1 map, so an
|
|
19
|
+
agent answering a question can explain a concept *through* the framing or translate either way. Wired
|
|
20
|
+
into the sidebar and home listing, and cross-linked from both source docs. Docs only — no code, types,
|
|
21
|
+
or package surface change.
|
|
22
|
+
|
|
23
|
+
- **Importable ESLint plugin (`@nhtio/adk/eslint`).** The harness's documented contracts are now
|
|
24
|
+
machine-checkable: a flat-config plugin that flags footguns the TypeScript compiler cannot see
|
|
25
|
+
because they live in runtime validators or conventions, not types. Five rules ship —
|
|
26
|
+
`require-validator-any-required` (a `validator.any()` chain with no explicit
|
|
27
|
+
`.required()`/`.optional()`/`.default()`/`.forbidden()` silently admits null/undefined),
|
|
28
|
+
`thought-payload-requires-replay-tag` (a `Thought` with a vendor `payload` but no
|
|
29
|
+
`replayCompatibility` can never be safely replayed), `token-encoding-requires-context-window` (a
|
|
30
|
+
Chat Completions adapter that counts tokens with no budget never runs its overflow guard),
|
|
31
|
+
`artifact-tool-forbids-artifact-constructor` (an `ArtifactTool` that wraps another artifact
|
|
32
|
+
recurses forever), and `no-model-in-tool-handler` (a model call inside a tool handler hides an
|
|
33
|
+
unmanaged dispatch — unless the handler runs its own scoped sub-agent via `new TurnRunner(...)` or
|
|
34
|
+
`DispatchRunner.dispatch(...)`). Import the assembled plugin
|
|
35
|
+
from `@nhtio/adk/eslint` (or `adk.configs.recommended` for all five), or individual rules from
|
|
36
|
+
`@nhtio/adk/eslint/rules/<name>`. `eslint` and `@typescript-eslint/utils` are **optional peers** —
|
|
37
|
+
installed only by consumers who lint with the plugin. Rules are report-only with inline
|
|
38
|
+
`eslint-disable` carve-outs. See the new **Developer Tools** docs section, which also now houses
|
|
39
|
+
the ADK Assembly MCP guide.
|
|
40
|
+
|
|
12
41
|
- **Vector conformance harness is now public (`@nhtio/adk/batteries/vector/conformance`).** The
|
|
13
42
|
`runVectorStoreConformance` suite (plus `stubEncoder` / `paddedStubEncoder`) that the 29 shipped
|
|
14
43
|
adapters test against is now an exported, deep-import-only subpath, so anyone writing their own
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
//#region src/eslint/rules/common.ts
|
|
3
|
+
/**
|
|
4
|
+
* Shared `createRule` factory for the published `@nhtio/adk` ESLint rules.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Wraps `ESLintUtils.RuleCreator` so every rule gets a consistent `meta.docs.url` pointing at the
|
|
8
|
+
* published rule reference. Each rule module imports this and default-exports `createRule({...})`.
|
|
9
|
+
*
|
|
10
|
+
* `@typescript-eslint/utils` is an OPTIONAL peer dependency of `@nhtio/adk` — it is only needed by
|
|
11
|
+
* consumers who import `@nhtio/adk/eslint`. The main library never imports this module.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Rule factory for `@nhtio/adk` ESLint rules. The `name` passed to each rule becomes the slug in the
|
|
15
|
+
* generated documentation URL.
|
|
16
|
+
*/
|
|
17
|
+
var createRule = ESLintUtils.RuleCreator((name) => `https://adk.nht.io/eslint/rules/${name}`);
|
|
18
|
+
var isFunctionNode = (node) => node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
19
|
+
var LLM_CTOR_NAMES = new Set([
|
|
20
|
+
"OpenAI",
|
|
21
|
+
"AzureOpenAI",
|
|
22
|
+
"Anthropic",
|
|
23
|
+
"AnthropicBedrock",
|
|
24
|
+
"AnthropicVertex",
|
|
25
|
+
"GoogleGenerativeAI",
|
|
26
|
+
"GoogleGenAI",
|
|
27
|
+
"Mistral",
|
|
28
|
+
"CohereClient",
|
|
29
|
+
"CohereClientV2",
|
|
30
|
+
"Groq"
|
|
31
|
+
]);
|
|
32
|
+
var LLM_METHOD_NAMES = new Set([
|
|
33
|
+
"generateContent",
|
|
34
|
+
"generateContentStream",
|
|
35
|
+
"generateMessage"
|
|
36
|
+
]);
|
|
37
|
+
var memberPropertyName = (node) => node.property.type === "Identifier" ? node.property.name : void 0;
|
|
38
|
+
/**
|
|
39
|
+
* Heuristic: does this call/new expression look like a primary LLM invocation? Detects known
|
|
40
|
+
* provider SDK constructors and `…create()` calls whose receiver chain passes through
|
|
41
|
+
* `chat`/`completions`/`messages`/`responses`, plus a small set of generate* methods. Deliberately
|
|
42
|
+
* conservative — the goal is to catch the obvious footgun, not to be exhaustive.
|
|
43
|
+
*/
|
|
44
|
+
var isLlmCall = (node) => {
|
|
45
|
+
if (node.type === "NewExpression") return node.callee.type === "Identifier" && LLM_CTOR_NAMES.has(node.callee.name);
|
|
46
|
+
if (node.type === "CallExpression" && node.callee.type === "MemberExpression") {
|
|
47
|
+
const method = memberPropertyName(node.callee);
|
|
48
|
+
if (method && LLM_METHOD_NAMES.has(method)) return true;
|
|
49
|
+
if (method === "create") {
|
|
50
|
+
let cur = node.callee.object;
|
|
51
|
+
while (cur.type === "MemberExpression") {
|
|
52
|
+
const seg = memberPropertyName(cur);
|
|
53
|
+
if (seg === "completions" || seg === "messages" || seg === "responses") return true;
|
|
54
|
+
cur = cur.object;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Walks the descendants of a function body (NOT crossing into nested function scopes) and invokes
|
|
62
|
+
* `visit` for every node, so a rule can scan a handler/middleware body for a pattern while ignoring
|
|
63
|
+
* inner closures (e.g. a `new TurnRunner` sub-agent's own callbacks).
|
|
64
|
+
*/
|
|
65
|
+
var walkBodySkippingNestedFunctions = (body, visit) => {
|
|
66
|
+
const recurse = (node) => {
|
|
67
|
+
visit(node);
|
|
68
|
+
for (const key of Object.keys(node)) {
|
|
69
|
+
if (key === "parent") continue;
|
|
70
|
+
const value = node[key];
|
|
71
|
+
const children = Array.isArray(value) ? value : [value];
|
|
72
|
+
for (const child of children) {
|
|
73
|
+
if (!child || typeof child !== "object") continue;
|
|
74
|
+
const childNode = child;
|
|
75
|
+
if (typeof childNode.type !== "string") continue;
|
|
76
|
+
if (isFunctionNode(childNode)) continue;
|
|
77
|
+
recurse(childNode);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
recurse(body);
|
|
82
|
+
};
|
|
83
|
+
var isSubAgentEntry = (node) => {
|
|
84
|
+
if (node.type === "NewExpression" && node.callee.type === "Identifier" && node.callee.name === "TurnRunner") return true;
|
|
85
|
+
if (node.type === "CallExpression" && node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier" && node.callee.property.name === "dispatch" && node.callee.object.type === "Identifier" && node.callee.object.name === "DispatchRunner") return true;
|
|
86
|
+
return false;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* True when the function body runs a scoped sub-agent — either `new TurnRunner(...)` or
|
|
90
|
+
* `DispatchRunner.dispatch(...)` — the documented escape hatch that exempts a tool handler from
|
|
91
|
+
* {@link isLlmCall} flagging.
|
|
92
|
+
*/
|
|
93
|
+
var runsSubAgent = (body) => {
|
|
94
|
+
let found = false;
|
|
95
|
+
const recurse = (node) => {
|
|
96
|
+
if (found) return;
|
|
97
|
+
if (isSubAgentEntry(node)) {
|
|
98
|
+
found = true;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
for (const key of Object.keys(node)) {
|
|
102
|
+
if (key === "parent") continue;
|
|
103
|
+
const value = node[key];
|
|
104
|
+
const children = Array.isArray(value) ? value : [value];
|
|
105
|
+
for (const child of children) {
|
|
106
|
+
if (!child || typeof child !== "object") continue;
|
|
107
|
+
const childNode = child;
|
|
108
|
+
if (typeof childNode.type !== "string") continue;
|
|
109
|
+
recurse(childNode);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
recurse(body);
|
|
114
|
+
return found;
|
|
115
|
+
};
|
|
116
|
+
//#endregion
|
|
117
|
+
export { walkBodySkippingNestedFunctions as a, runsSubAgent as i, isFunctionNode as n, isLlmCall as r, createRule as t };
|
|
118
|
+
|
|
119
|
+
//# sourceMappingURL=common-BUIjZ6LV.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common-BUIjZ6LV.mjs","names":[],"sources":["../src/eslint/rules/common.ts"],"sourcesContent":["/**\n * Shared `createRule` factory for the published `@nhtio/adk` ESLint rules.\n *\n * @remarks\n * Wraps `ESLintUtils.RuleCreator` so every rule gets a consistent `meta.docs.url` pointing at the\n * published rule reference. Each rule module imports this and default-exports `createRule({...})`.\n *\n * `@typescript-eslint/utils` is an OPTIONAL peer dependency of `@nhtio/adk` — it is only needed by\n * consumers who import `@nhtio/adk/eslint`. The main library never imports this module.\n */\n\nimport { ESLintUtils } from '@typescript-eslint/utils'\n\nimport type { TSESTree } from '@typescript-eslint/utils'\n\n/**\n * Rule factory for `@nhtio/adk` ESLint rules. The `name` passed to each rule becomes the slug in the\n * generated documentation URL.\n */\nexport const createRule = ESLintUtils.RuleCreator(\n (name) => `https://adk.nht.io/eslint/rules/${name}`\n)\n\n/** Any function-like AST node (arrow, function expression, or declaration). */\nexport type FunctionNode =\n | TSESTree.FunctionDeclaration\n | TSESTree.FunctionExpression\n | TSESTree.ArrowFunctionExpression\n\nexport const isFunctionNode = (node: TSESTree.Node): node is FunctionNode =>\n node.type === 'FunctionDeclaration' ||\n node.type === 'FunctionExpression' ||\n node.type === 'ArrowFunctionExpression'\n\n// Provider SDK constructor names whose presence inside a handler/middleware indicates a primary\n// model call. Conservative, well-known set — keeps false positives low.\nconst LLM_CTOR_NAMES = new Set([\n 'OpenAI',\n 'AzureOpenAI',\n 'Anthropic',\n 'AnthropicBedrock',\n 'AnthropicVertex',\n 'GoogleGenerativeAI',\n 'GoogleGenAI',\n 'Mistral',\n 'CohereClient',\n 'CohereClientV2',\n 'Groq',\n])\n\n// Method-name tails that indicate a chat/completion/generation call on a provider client, e.g.\n// `client.chat.completions.create(...)`, `client.messages.create(...)`, `model.generateContent(...)`.\nconst LLM_METHOD_NAMES = new Set(['generateContent', 'generateContentStream', 'generateMessage'])\n\nconst memberPropertyName = (node: TSESTree.MemberExpression): string | undefined =>\n node.property.type === 'Identifier' ? node.property.name : undefined\n\n/**\n * Heuristic: does this call/new expression look like a primary LLM invocation? Detects known\n * provider SDK constructors and `…create()` calls whose receiver chain passes through\n * `chat`/`completions`/`messages`/`responses`, plus a small set of generate* methods. Deliberately\n * conservative — the goal is to catch the obvious footgun, not to be exhaustive.\n */\nexport const isLlmCall = (node: TSESTree.Node): boolean => {\n if (node.type === 'NewExpression') {\n return node.callee.type === 'Identifier' && LLM_CTOR_NAMES.has(node.callee.name)\n }\n if (node.type === 'CallExpression' && node.callee.type === 'MemberExpression') {\n const method = memberPropertyName(node.callee)\n if (method && LLM_METHOD_NAMES.has(method)) return true\n // `<chain>.create()` where the chain passes through a provider sub-resource.\n if (method === 'create') {\n let cur: TSESTree.Node = node.callee.object\n while (cur.type === 'MemberExpression') {\n const seg = memberPropertyName(cur)\n if (seg === 'completions' || seg === 'messages' || seg === 'responses') return true\n cur = cur.object\n }\n }\n }\n return false\n}\n\n/**\n * Walks the descendants of a function body (NOT crossing into nested function scopes) and invokes\n * `visit` for every node, so a rule can scan a handler/middleware body for a pattern while ignoring\n * inner closures (e.g. a `new TurnRunner` sub-agent's own callbacks).\n */\nexport const walkBodySkippingNestedFunctions = (\n body: TSESTree.Node,\n visit: (node: TSESTree.Node) => void\n): void => {\n const recurse = (node: TSESTree.Node): void => {\n visit(node)\n for (const key of Object.keys(node)) {\n if (key === 'parent') continue\n const value = (node as unknown as Record<string, unknown>)[key]\n const children = Array.isArray(value) ? value : [value]\n for (const child of children) {\n if (!child || typeof child !== 'object') continue\n const childNode = child as TSESTree.Node\n if (typeof childNode.type !== 'string') continue\n // Do not descend into nested function scopes — their calls belong to a different context.\n if (isFunctionNode(childNode)) continue\n recurse(childNode)\n }\n }\n }\n recurse(body)\n}\n\n// A sub-agent is run through one of two blessed entry points: constructing a scoped\n// `new TurnRunner(...)`, or the lower-level static `DispatchRunner.dispatch(...)` (its constructor\n// is token-gated private, so `dispatch()` is the real entry point). Either one inside a tool handler\n// means \"this tool is deliberately a sub-agent,\" which is the documented exception to\n// no-model-in-tool-handler.\nconst isSubAgentEntry = (node: TSESTree.Node): boolean => {\n // new TurnRunner(...)\n if (\n node.type === 'NewExpression' &&\n node.callee.type === 'Identifier' &&\n node.callee.name === 'TurnRunner'\n ) {\n return true\n }\n // DispatchRunner.dispatch(...)\n if (\n node.type === 'CallExpression' &&\n node.callee.type === 'MemberExpression' &&\n node.callee.property.type === 'Identifier' &&\n node.callee.property.name === 'dispatch' &&\n node.callee.object.type === 'Identifier' &&\n node.callee.object.name === 'DispatchRunner'\n ) {\n return true\n }\n return false\n}\n\n/**\n * True when the function body runs a scoped sub-agent — either `new TurnRunner(...)` or\n * `DispatchRunner.dispatch(...)` — the documented escape hatch that exempts a tool handler from\n * {@link isLlmCall} flagging.\n */\nexport const runsSubAgent = (body: TSESTree.Node): boolean => {\n let found = false\n const recurse = (node: TSESTree.Node): void => {\n if (found) return\n if (isSubAgentEntry(node)) {\n found = true\n return\n }\n for (const key of Object.keys(node)) {\n if (key === 'parent') continue\n const value = (node as unknown as Record<string, unknown>)[key]\n const children = Array.isArray(value) ? value : [value]\n for (const child of children) {\n if (!child || typeof child !== 'object') continue\n const childNode = child as TSESTree.Node\n if (typeof childNode.type !== 'string') continue\n recurse(childNode)\n }\n }\n }\n recurse(body)\n return found\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,IAAa,aAAa,YAAY,aACnC,SAAS,mCAAmC,MAC/C;AAQA,IAAa,kBAAkB,SAC7B,KAAK,SAAS,yBACd,KAAK,SAAS,wBACd,KAAK,SAAS;AAIhB,IAAM,iBAAiB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAID,IAAM,mBAAmB,IAAI,IAAI;CAAC;CAAmB;CAAyB;AAAiB,CAAC;AAEhG,IAAM,sBAAsB,SAC1B,KAAK,SAAS,SAAS,eAAe,KAAK,SAAS,OAAO,KAAA;;;;;;;AAQ7D,IAAa,aAAa,SAAiC;CACzD,IAAI,KAAK,SAAS,iBAChB,OAAO,KAAK,OAAO,SAAS,gBAAgB,eAAe,IAAI,KAAK,OAAO,IAAI;CAEjF,IAAI,KAAK,SAAS,oBAAoB,KAAK,OAAO,SAAS,oBAAoB;EAC7E,MAAM,SAAS,mBAAmB,KAAK,MAAM;EAC7C,IAAI,UAAU,iBAAiB,IAAI,MAAM,GAAG,OAAO;EAEnD,IAAI,WAAW,UAAU;GACvB,IAAI,MAAqB,KAAK,OAAO;GACrC,OAAO,IAAI,SAAS,oBAAoB;IACtC,MAAM,MAAM,mBAAmB,GAAG;IAClC,IAAI,QAAQ,iBAAiB,QAAQ,cAAc,QAAQ,aAAa,OAAO;IAC/E,MAAM,IAAI;GACZ;EACF;CACF;CACA,OAAO;AACT;;;;;;AAOA,IAAa,mCACX,MACA,UACS;CACT,MAAM,WAAW,SAA8B;EAC7C,MAAM,IAAI;EACV,KAAK,MAAM,OAAO,OAAO,KAAK,IAAI,GAAG;GACnC,IAAI,QAAQ,UAAU;GACtB,MAAM,QAAS,KAA4C;GAC3D,MAAM,WAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;GACtD,KAAK,MAAM,SAAS,UAAU;IAC5B,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU;IACzC,MAAM,YAAY;IAClB,IAAI,OAAO,UAAU,SAAS,UAAU;IAExC,IAAI,eAAe,SAAS,GAAG;IAC/B,QAAQ,SAAS;GACnB;EACF;CACF;CACA,QAAQ,IAAI;AACd;AAOA,IAAM,mBAAmB,SAAiC;CAExD,IACE,KAAK,SAAS,mBACd,KAAK,OAAO,SAAS,gBACrB,KAAK,OAAO,SAAS,cAErB,OAAO;CAGT,IACE,KAAK,SAAS,oBACd,KAAK,OAAO,SAAS,sBACrB,KAAK,OAAO,SAAS,SAAS,gBAC9B,KAAK,OAAO,SAAS,SAAS,cAC9B,KAAK,OAAO,OAAO,SAAS,gBAC5B,KAAK,OAAO,OAAO,SAAS,kBAE5B,OAAO;CAET,OAAO;AACT;;;;;;AAOA,IAAa,gBAAgB,SAAiC;CAC5D,IAAI,QAAQ;CACZ,MAAM,WAAW,SAA8B;EAC7C,IAAI,OAAO;EACX,IAAI,gBAAgB,IAAI,GAAG;GACzB,QAAQ;GACR;EACF;EACA,KAAK,MAAM,OAAO,OAAO,KAAK,IAAI,GAAG;GACnC,IAAI,QAAQ,UAAU;GACtB,MAAM,QAAS,KAA4C;GAC3D,MAAM,WAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;GACtD,KAAK,MAAM,SAAS,UAAU;IAC5B,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU;IACzC,MAAM,YAAY;IAClB,IAAI,OAAO,UAAU,SAAS,UAAU;IACxC,QAAQ,SAAS;GACnB;EACF;CACF;CACA,QAAQ,IAAI;CACZ,OAAO;AACT"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
require("./chunk-Ble4zEEl.js");
|
|
2
|
+
//#region src/eslint/rules/common.ts
|
|
3
|
+
/**
|
|
4
|
+
* Shared `createRule` factory for the published `@nhtio/adk` ESLint rules.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Wraps `ESLintUtils.RuleCreator` so every rule gets a consistent `meta.docs.url` pointing at the
|
|
8
|
+
* published rule reference. Each rule module imports this and default-exports `createRule({...})`.
|
|
9
|
+
*
|
|
10
|
+
* `@typescript-eslint/utils` is an OPTIONAL peer dependency of `@nhtio/adk` — it is only needed by
|
|
11
|
+
* consumers who import `@nhtio/adk/eslint`. The main library never imports this module.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Rule factory for `@nhtio/adk` ESLint rules. The `name` passed to each rule becomes the slug in the
|
|
15
|
+
* generated documentation URL.
|
|
16
|
+
*/
|
|
17
|
+
var createRule = require("@typescript-eslint/utils").ESLintUtils.RuleCreator((name) => `https://adk.nht.io/eslint/rules/${name}`);
|
|
18
|
+
var isFunctionNode = (node) => node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
19
|
+
var LLM_CTOR_NAMES = new Set([
|
|
20
|
+
"OpenAI",
|
|
21
|
+
"AzureOpenAI",
|
|
22
|
+
"Anthropic",
|
|
23
|
+
"AnthropicBedrock",
|
|
24
|
+
"AnthropicVertex",
|
|
25
|
+
"GoogleGenerativeAI",
|
|
26
|
+
"GoogleGenAI",
|
|
27
|
+
"Mistral",
|
|
28
|
+
"CohereClient",
|
|
29
|
+
"CohereClientV2",
|
|
30
|
+
"Groq"
|
|
31
|
+
]);
|
|
32
|
+
var LLM_METHOD_NAMES = new Set([
|
|
33
|
+
"generateContent",
|
|
34
|
+
"generateContentStream",
|
|
35
|
+
"generateMessage"
|
|
36
|
+
]);
|
|
37
|
+
var memberPropertyName = (node) => node.property.type === "Identifier" ? node.property.name : void 0;
|
|
38
|
+
/**
|
|
39
|
+
* Heuristic: does this call/new expression look like a primary LLM invocation? Detects known
|
|
40
|
+
* provider SDK constructors and `…create()` calls whose receiver chain passes through
|
|
41
|
+
* `chat`/`completions`/`messages`/`responses`, plus a small set of generate* methods. Deliberately
|
|
42
|
+
* conservative — the goal is to catch the obvious footgun, not to be exhaustive.
|
|
43
|
+
*/
|
|
44
|
+
var isLlmCall = (node) => {
|
|
45
|
+
if (node.type === "NewExpression") return node.callee.type === "Identifier" && LLM_CTOR_NAMES.has(node.callee.name);
|
|
46
|
+
if (node.type === "CallExpression" && node.callee.type === "MemberExpression") {
|
|
47
|
+
const method = memberPropertyName(node.callee);
|
|
48
|
+
if (method && LLM_METHOD_NAMES.has(method)) return true;
|
|
49
|
+
if (method === "create") {
|
|
50
|
+
let cur = node.callee.object;
|
|
51
|
+
while (cur.type === "MemberExpression") {
|
|
52
|
+
const seg = memberPropertyName(cur);
|
|
53
|
+
if (seg === "completions" || seg === "messages" || seg === "responses") return true;
|
|
54
|
+
cur = cur.object;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Walks the descendants of a function body (NOT crossing into nested function scopes) and invokes
|
|
62
|
+
* `visit` for every node, so a rule can scan a handler/middleware body for a pattern while ignoring
|
|
63
|
+
* inner closures (e.g. a `new TurnRunner` sub-agent's own callbacks).
|
|
64
|
+
*/
|
|
65
|
+
var walkBodySkippingNestedFunctions = (body, visit) => {
|
|
66
|
+
const recurse = (node) => {
|
|
67
|
+
visit(node);
|
|
68
|
+
for (const key of Object.keys(node)) {
|
|
69
|
+
if (key === "parent") continue;
|
|
70
|
+
const value = node[key];
|
|
71
|
+
const children = Array.isArray(value) ? value : [value];
|
|
72
|
+
for (const child of children) {
|
|
73
|
+
if (!child || typeof child !== "object") continue;
|
|
74
|
+
const childNode = child;
|
|
75
|
+
if (typeof childNode.type !== "string") continue;
|
|
76
|
+
if (isFunctionNode(childNode)) continue;
|
|
77
|
+
recurse(childNode);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
recurse(body);
|
|
82
|
+
};
|
|
83
|
+
var isSubAgentEntry = (node) => {
|
|
84
|
+
if (node.type === "NewExpression" && node.callee.type === "Identifier" && node.callee.name === "TurnRunner") return true;
|
|
85
|
+
if (node.type === "CallExpression" && node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier" && node.callee.property.name === "dispatch" && node.callee.object.type === "Identifier" && node.callee.object.name === "DispatchRunner") return true;
|
|
86
|
+
return false;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* True when the function body runs a scoped sub-agent — either `new TurnRunner(...)` or
|
|
90
|
+
* `DispatchRunner.dispatch(...)` — the documented escape hatch that exempts a tool handler from
|
|
91
|
+
* {@link isLlmCall} flagging.
|
|
92
|
+
*/
|
|
93
|
+
var runsSubAgent = (body) => {
|
|
94
|
+
let found = false;
|
|
95
|
+
const recurse = (node) => {
|
|
96
|
+
if (found) return;
|
|
97
|
+
if (isSubAgentEntry(node)) {
|
|
98
|
+
found = true;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
for (const key of Object.keys(node)) {
|
|
102
|
+
if (key === "parent") continue;
|
|
103
|
+
const value = node[key];
|
|
104
|
+
const children = Array.isArray(value) ? value : [value];
|
|
105
|
+
for (const child of children) {
|
|
106
|
+
if (!child || typeof child !== "object") continue;
|
|
107
|
+
const childNode = child;
|
|
108
|
+
if (typeof childNode.type !== "string") continue;
|
|
109
|
+
recurse(childNode);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
recurse(body);
|
|
114
|
+
return found;
|
|
115
|
+
};
|
|
116
|
+
//#endregion
|
|
117
|
+
Object.defineProperty(exports, "createRule", {
|
|
118
|
+
enumerable: true,
|
|
119
|
+
get: function() {
|
|
120
|
+
return createRule;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
Object.defineProperty(exports, "isFunctionNode", {
|
|
124
|
+
enumerable: true,
|
|
125
|
+
get: function() {
|
|
126
|
+
return isFunctionNode;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
Object.defineProperty(exports, "isLlmCall", {
|
|
130
|
+
enumerable: true,
|
|
131
|
+
get: function() {
|
|
132
|
+
return isLlmCall;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
Object.defineProperty(exports, "runsSubAgent", {
|
|
136
|
+
enumerable: true,
|
|
137
|
+
get: function() {
|
|
138
|
+
return runsSubAgent;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
Object.defineProperty(exports, "walkBodySkippingNestedFunctions", {
|
|
142
|
+
enumerable: true,
|
|
143
|
+
get: function() {
|
|
144
|
+
return walkBodySkippingNestedFunctions;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
//# sourceMappingURL=common-DeNipTQI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common-DeNipTQI.js","names":[],"sources":["../src/eslint/rules/common.ts"],"sourcesContent":["/**\n * Shared `createRule` factory for the published `@nhtio/adk` ESLint rules.\n *\n * @remarks\n * Wraps `ESLintUtils.RuleCreator` so every rule gets a consistent `meta.docs.url` pointing at the\n * published rule reference. Each rule module imports this and default-exports `createRule({...})`.\n *\n * `@typescript-eslint/utils` is an OPTIONAL peer dependency of `@nhtio/adk` — it is only needed by\n * consumers who import `@nhtio/adk/eslint`. The main library never imports this module.\n */\n\nimport { ESLintUtils } from '@typescript-eslint/utils'\n\nimport type { TSESTree } from '@typescript-eslint/utils'\n\n/**\n * Rule factory for `@nhtio/adk` ESLint rules. The `name` passed to each rule becomes the slug in the\n * generated documentation URL.\n */\nexport const createRule = ESLintUtils.RuleCreator(\n (name) => `https://adk.nht.io/eslint/rules/${name}`\n)\n\n/** Any function-like AST node (arrow, function expression, or declaration). */\nexport type FunctionNode =\n | TSESTree.FunctionDeclaration\n | TSESTree.FunctionExpression\n | TSESTree.ArrowFunctionExpression\n\nexport const isFunctionNode = (node: TSESTree.Node): node is FunctionNode =>\n node.type === 'FunctionDeclaration' ||\n node.type === 'FunctionExpression' ||\n node.type === 'ArrowFunctionExpression'\n\n// Provider SDK constructor names whose presence inside a handler/middleware indicates a primary\n// model call. Conservative, well-known set — keeps false positives low.\nconst LLM_CTOR_NAMES = new Set([\n 'OpenAI',\n 'AzureOpenAI',\n 'Anthropic',\n 'AnthropicBedrock',\n 'AnthropicVertex',\n 'GoogleGenerativeAI',\n 'GoogleGenAI',\n 'Mistral',\n 'CohereClient',\n 'CohereClientV2',\n 'Groq',\n])\n\n// Method-name tails that indicate a chat/completion/generation call on a provider client, e.g.\n// `client.chat.completions.create(...)`, `client.messages.create(...)`, `model.generateContent(...)`.\nconst LLM_METHOD_NAMES = new Set(['generateContent', 'generateContentStream', 'generateMessage'])\n\nconst memberPropertyName = (node: TSESTree.MemberExpression): string | undefined =>\n node.property.type === 'Identifier' ? node.property.name : undefined\n\n/**\n * Heuristic: does this call/new expression look like a primary LLM invocation? Detects known\n * provider SDK constructors and `…create()` calls whose receiver chain passes through\n * `chat`/`completions`/`messages`/`responses`, plus a small set of generate* methods. Deliberately\n * conservative — the goal is to catch the obvious footgun, not to be exhaustive.\n */\nexport const isLlmCall = (node: TSESTree.Node): boolean => {\n if (node.type === 'NewExpression') {\n return node.callee.type === 'Identifier' && LLM_CTOR_NAMES.has(node.callee.name)\n }\n if (node.type === 'CallExpression' && node.callee.type === 'MemberExpression') {\n const method = memberPropertyName(node.callee)\n if (method && LLM_METHOD_NAMES.has(method)) return true\n // `<chain>.create()` where the chain passes through a provider sub-resource.\n if (method === 'create') {\n let cur: TSESTree.Node = node.callee.object\n while (cur.type === 'MemberExpression') {\n const seg = memberPropertyName(cur)\n if (seg === 'completions' || seg === 'messages' || seg === 'responses') return true\n cur = cur.object\n }\n }\n }\n return false\n}\n\n/**\n * Walks the descendants of a function body (NOT crossing into nested function scopes) and invokes\n * `visit` for every node, so a rule can scan a handler/middleware body for a pattern while ignoring\n * inner closures (e.g. a `new TurnRunner` sub-agent's own callbacks).\n */\nexport const walkBodySkippingNestedFunctions = (\n body: TSESTree.Node,\n visit: (node: TSESTree.Node) => void\n): void => {\n const recurse = (node: TSESTree.Node): void => {\n visit(node)\n for (const key of Object.keys(node)) {\n if (key === 'parent') continue\n const value = (node as unknown as Record<string, unknown>)[key]\n const children = Array.isArray(value) ? value : [value]\n for (const child of children) {\n if (!child || typeof child !== 'object') continue\n const childNode = child as TSESTree.Node\n if (typeof childNode.type !== 'string') continue\n // Do not descend into nested function scopes — their calls belong to a different context.\n if (isFunctionNode(childNode)) continue\n recurse(childNode)\n }\n }\n }\n recurse(body)\n}\n\n// A sub-agent is run through one of two blessed entry points: constructing a scoped\n// `new TurnRunner(...)`, or the lower-level static `DispatchRunner.dispatch(...)` (its constructor\n// is token-gated private, so `dispatch()` is the real entry point). Either one inside a tool handler\n// means \"this tool is deliberately a sub-agent,\" which is the documented exception to\n// no-model-in-tool-handler.\nconst isSubAgentEntry = (node: TSESTree.Node): boolean => {\n // new TurnRunner(...)\n if (\n node.type === 'NewExpression' &&\n node.callee.type === 'Identifier' &&\n node.callee.name === 'TurnRunner'\n ) {\n return true\n }\n // DispatchRunner.dispatch(...)\n if (\n node.type === 'CallExpression' &&\n node.callee.type === 'MemberExpression' &&\n node.callee.property.type === 'Identifier' &&\n node.callee.property.name === 'dispatch' &&\n node.callee.object.type === 'Identifier' &&\n node.callee.object.name === 'DispatchRunner'\n ) {\n return true\n }\n return false\n}\n\n/**\n * True when the function body runs a scoped sub-agent — either `new TurnRunner(...)` or\n * `DispatchRunner.dispatch(...)` — the documented escape hatch that exempts a tool handler from\n * {@link isLlmCall} flagging.\n */\nexport const runsSubAgent = (body: TSESTree.Node): boolean => {\n let found = false\n const recurse = (node: TSESTree.Node): void => {\n if (found) return\n if (isSubAgentEntry(node)) {\n found = true\n return\n }\n for (const key of Object.keys(node)) {\n if (key === 'parent') continue\n const value = (node as unknown as Record<string, unknown>)[key]\n const children = Array.isArray(value) ? value : [value]\n for (const child of children) {\n if (!child || typeof child !== 'object') continue\n const childNode = child as TSESTree.Node\n if (typeof childNode.type !== 'string') continue\n recurse(childNode)\n }\n }\n }\n recurse(body)\n return found\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,IAAa,+CAAa,EAAA,YAAY,aACnC,SAAS,mCAAmC,MAC/C;AAQA,IAAa,kBAAkB,SAC7B,KAAK,SAAS,yBACd,KAAK,SAAS,wBACd,KAAK,SAAS;AAIhB,IAAM,iBAAiB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAID,IAAM,mBAAmB,IAAI,IAAI;CAAC;CAAmB;CAAyB;AAAiB,CAAC;AAEhG,IAAM,sBAAsB,SAC1B,KAAK,SAAS,SAAS,eAAe,KAAK,SAAS,OAAO,KAAA;;;;;;;AAQ7D,IAAa,aAAa,SAAiC;CACzD,IAAI,KAAK,SAAS,iBAChB,OAAO,KAAK,OAAO,SAAS,gBAAgB,eAAe,IAAI,KAAK,OAAO,IAAI;CAEjF,IAAI,KAAK,SAAS,oBAAoB,KAAK,OAAO,SAAS,oBAAoB;EAC7E,MAAM,SAAS,mBAAmB,KAAK,MAAM;EAC7C,IAAI,UAAU,iBAAiB,IAAI,MAAM,GAAG,OAAO;EAEnD,IAAI,WAAW,UAAU;GACvB,IAAI,MAAqB,KAAK,OAAO;GACrC,OAAO,IAAI,SAAS,oBAAoB;IACtC,MAAM,MAAM,mBAAmB,GAAG;IAClC,IAAI,QAAQ,iBAAiB,QAAQ,cAAc,QAAQ,aAAa,OAAO;IAC/E,MAAM,IAAI;GACZ;EACF;CACF;CACA,OAAO;AACT;;;;;;AAOA,IAAa,mCACX,MACA,UACS;CACT,MAAM,WAAW,SAA8B;EAC7C,MAAM,IAAI;EACV,KAAK,MAAM,OAAO,OAAO,KAAK,IAAI,GAAG;GACnC,IAAI,QAAQ,UAAU;GACtB,MAAM,QAAS,KAA4C;GAC3D,MAAM,WAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;GACtD,KAAK,MAAM,SAAS,UAAU;IAC5B,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU;IACzC,MAAM,YAAY;IAClB,IAAI,OAAO,UAAU,SAAS,UAAU;IAExC,IAAI,eAAe,SAAS,GAAG;IAC/B,QAAQ,SAAS;GACnB;EACF;CACF;CACA,QAAQ,IAAI;AACd;AAOA,IAAM,mBAAmB,SAAiC;CAExD,IACE,KAAK,SAAS,mBACd,KAAK,OAAO,SAAS,gBACrB,KAAK,OAAO,SAAS,cAErB,OAAO;CAGT,IACE,KAAK,SAAS,oBACd,KAAK,OAAO,SAAS,sBACrB,KAAK,OAAO,SAAS,SAAS,gBAC9B,KAAK,OAAO,SAAS,SAAS,cAC9B,KAAK,OAAO,OAAO,SAAS,gBAC5B,KAAK,OAAO,OAAO,SAAS,kBAE5B,OAAO;CAET,OAAO;AACT;;;;;;AAOA,IAAa,gBAAgB,SAAiC;CAC5D,IAAI,QAAQ;CACZ,MAAM,WAAW,SAA8B;EAC7C,IAAI,OAAO;EACX,IAAI,gBAAgB,IAAI,GAAG;GACzB,QAAQ;GACR;EACF;EACA,KAAK,MAAM,OAAO,OAAO,KAAK,IAAI,GAAG;GACnC,IAAI,QAAQ,UAAU;GACtB,MAAM,QAAS,KAA4C;GAC3D,MAAM,WAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;GACtD,KAAK,MAAM,SAAS,UAAU;IAC5B,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU;IACzC,MAAM,YAAY;IAClB,IAAI,OAAO,UAAU,SAAS,UAAU;IACxC,QAAQ,SAAS;GACnB;EACF;CACF;CACA,QAAQ,IAAI;CACZ,OAAO;AACT"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @nhtio/adk/eslint
|
|
3
|
+
*
|
|
4
|
+
* The `@nhtio/adk` ESLint plugin: machine-checkable enforcement of the harness contracts that
|
|
5
|
+
* implementors of bring-your-own batteries, tools, and primitives are most likely to get wrong.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* These rules turn documented footguns into lint errors. They are report-only (no autofix) — each
|
|
9
|
+
* names the contract it enforces and the fix; carve out a deliberate exception with an inline
|
|
10
|
+
* `// eslint-disable-next-line adk/<rule> -- <reason>` comment.
|
|
11
|
+
*
|
|
12
|
+
* `@typescript-eslint/utils` and `eslint` are OPTIONAL peer dependencies of `@nhtio/adk` — installed
|
|
13
|
+
* only by consumers who lint with this plugin. The main library never imports this module.
|
|
14
|
+
*
|
|
15
|
+
* @example Flat config
|
|
16
|
+
* ```ts
|
|
17
|
+
* import adk from '@nhtio/adk/eslint'
|
|
18
|
+
*
|
|
19
|
+
* export default [
|
|
20
|
+
* { plugins: { adk: adk.plugin }, rules: adk.configs.recommended.rules },
|
|
21
|
+
* ]
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint';
|
|
25
|
+
/**
|
|
26
|
+
* Map of rule id (without the `adk/` plugin prefix) to its rule object. Registered on the plugin as
|
|
27
|
+
* `rules`, so configs reference them as `adk/<id>`.
|
|
28
|
+
*/
|
|
29
|
+
export declare const rules: {
|
|
30
|
+
'require-validator-any-required': import("@typescript-eslint/utils/ts-eslint").RuleModule<"declareIntent", [
|
|
31
|
+
], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
32
|
+
name: string;
|
|
33
|
+
};
|
|
34
|
+
'no-model-in-tool-handler': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noModelInHandler", [
|
|
35
|
+
], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
36
|
+
name: string;
|
|
37
|
+
};
|
|
38
|
+
'thought-payload-requires-replay-tag': import("@typescript-eslint/utils/ts-eslint").RuleModule<"requireReplayTag", [
|
|
39
|
+
], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
40
|
+
name: string;
|
|
41
|
+
};
|
|
42
|
+
'token-encoding-requires-context-window': import("@typescript-eslint/utils/ts-eslint").RuleModule<"requireContextWindow", [
|
|
43
|
+
], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
44
|
+
name: string;
|
|
45
|
+
};
|
|
46
|
+
'artifact-tool-forbids-artifact-constructor': import("@typescript-eslint/utils/ts-eslint").RuleModule<"forbidArtifactConstructor", [
|
|
47
|
+
], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
48
|
+
name: string;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* The ESLint plugin object. Register under the `adk` namespace: `plugins: { adk: plugin }`.
|
|
53
|
+
*/
|
|
54
|
+
export declare const plugin: FlatConfig.Plugin;
|
|
55
|
+
/**
|
|
56
|
+
* Named config presets. `recommended` enables every rule at `error` and registers the plugin under
|
|
57
|
+
* the `adk` namespace — spread it into a flat config to adopt the full set.
|
|
58
|
+
*/
|
|
59
|
+
export declare const configs: {
|
|
60
|
+
recommended: {
|
|
61
|
+
name: string;
|
|
62
|
+
plugins: {
|
|
63
|
+
adk: FlatConfig.Plugin;
|
|
64
|
+
};
|
|
65
|
+
rules: Partial<Record<string, import("@typescript-eslint/utils/ts-eslint").SharedConfig.RuleEntry>>;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Default export bundles the plugin and its config presets, mirroring the shape ESLint flat configs
|
|
70
|
+
* expect from a plugin module.
|
|
71
|
+
*/
|
|
72
|
+
declare const _default: {
|
|
73
|
+
plugin: FlatConfig.Plugin;
|
|
74
|
+
rules: {
|
|
75
|
+
'require-validator-any-required': import("@typescript-eslint/utils/ts-eslint").RuleModule<"declareIntent", [
|
|
76
|
+
], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
77
|
+
name: string;
|
|
78
|
+
};
|
|
79
|
+
'no-model-in-tool-handler': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noModelInHandler", [
|
|
80
|
+
], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
81
|
+
name: string;
|
|
82
|
+
};
|
|
83
|
+
'thought-payload-requires-replay-tag': import("@typescript-eslint/utils/ts-eslint").RuleModule<"requireReplayTag", [
|
|
84
|
+
], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
85
|
+
name: string;
|
|
86
|
+
};
|
|
87
|
+
'token-encoding-requires-context-window': import("@typescript-eslint/utils/ts-eslint").RuleModule<"requireContextWindow", [
|
|
88
|
+
], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
89
|
+
name: string;
|
|
90
|
+
};
|
|
91
|
+
'artifact-tool-forbids-artifact-constructor': import("@typescript-eslint/utils/ts-eslint").RuleModule<"forbidArtifactConstructor", [
|
|
92
|
+
], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
93
|
+
name: string;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
configs: {
|
|
97
|
+
recommended: {
|
|
98
|
+
name: string;
|
|
99
|
+
plugins: {
|
|
100
|
+
adk: FlatConfig.Plugin;
|
|
101
|
+
};
|
|
102
|
+
rules: Partial<Record<string, import("@typescript-eslint/utils/ts-eslint").SharedConfig.RuleEntry>>;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
export default _default;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
require("../../chunk-Ble4zEEl.js");
|
|
6
|
+
const require_common = require("../../common-DeNipTQI.js");
|
|
7
|
+
//#region src/eslint/rules/artifact_tool_forbids_artifact_constructor.ts
|
|
8
|
+
/**
|
|
9
|
+
* @module @nhtio/adk/eslint/rules/artifact_tool_forbids_artifact_constructor
|
|
10
|
+
*
|
|
11
|
+
* Flags `new ArtifactTool({ … })` whose options literal sets an `artifactConstructor`.
|
|
12
|
+
*
|
|
13
|
+
* Why: an `ArtifactTool` is the tool that answers queries AGAINST a spooled artifact — it must not
|
|
14
|
+
* itself return a `SpooledArtifact`, or the result would be re-wrapped into another artifact whose
|
|
15
|
+
* query tools are themselves `ArtifactTool`s, and so on without end. The base `RawTool` accepts an
|
|
16
|
+
* `artifactConstructor` (the subclass used to wrap a tool's bytes), but `ArtifactTool` explicitly
|
|
17
|
+
* FORBIDS it (`artifactConstructor: validator.any().forbidden()` in its schema). Supplying one is a
|
|
18
|
+
* recursion footgun the constructor rejects at runtime; this rule surfaces it at the construction
|
|
19
|
+
* site so it fails lint.
|
|
20
|
+
*
|
|
21
|
+
* Opt-out:
|
|
22
|
+
* // eslint-disable-next-line adk/artifact-tool-forbids-artifact-constructor -- <reason>
|
|
23
|
+
*/
|
|
24
|
+
var literalKey = (prop) => {
|
|
25
|
+
if (prop.computed) return void 0;
|
|
26
|
+
if (prop.key.type === "Identifier") return prop.key.name;
|
|
27
|
+
if (prop.key.type === "Literal" && typeof prop.key.value === "string") return prop.key.value;
|
|
28
|
+
};
|
|
29
|
+
var isUndefinedValue = (node) => node.type === "Identifier" && node.name === "undefined";
|
|
30
|
+
var artifactToolForbidsArtifactConstructorRule = require_common.createRule({
|
|
31
|
+
name: "artifact-tool-forbids-artifact-constructor",
|
|
32
|
+
meta: {
|
|
33
|
+
type: "problem",
|
|
34
|
+
docs: { description: "An `ArtifactTool` must not declare `artifactConstructor` — it answers queries against an artifact and cannot itself return one (infinite re-wrapping). The ArtifactTool schema forbids the field at runtime; this surfaces it at the construction site." },
|
|
35
|
+
schema: [],
|
|
36
|
+
messages: { forbidArtifactConstructor: "`new ArtifactTool` must not set `artifactConstructor` — an artifact-query tool cannot itself return a SpooledArtifact (it would re-wrap forever). The ArtifactTool schema forbids this field. Remove it. Opt out with an eslint-disable-next-line adk/artifact-tool-forbids-artifact-constructor comment + reason." }
|
|
37
|
+
},
|
|
38
|
+
defaultOptions: [],
|
|
39
|
+
create(context) {
|
|
40
|
+
return { NewExpression(node) {
|
|
41
|
+
if (node.callee.type !== "Identifier" || node.callee.name !== "ArtifactTool") return;
|
|
42
|
+
const arg = node.arguments[0];
|
|
43
|
+
if (!arg || arg.type !== "ObjectExpression") return;
|
|
44
|
+
for (const p of arg.properties) {
|
|
45
|
+
if (p.type === "SpreadElement") continue;
|
|
46
|
+
if (literalKey(p) === "artifactConstructor" && !isUndefinedValue(p.value)) context.report({
|
|
47
|
+
node: p,
|
|
48
|
+
messageId: "forbidArtifactConstructor"
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
} };
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
//#endregion
|
|
55
|
+
exports.default = artifactToolForbidsArtifactConstructorRule;
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=artifact_tool_forbids_artifact_constructor.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact_tool_forbids_artifact_constructor.cjs","names":[],"sources":["../../../src/eslint/rules/artifact_tool_forbids_artifact_constructor.ts"],"sourcesContent":["/**\n * @module @nhtio/adk/eslint/rules/artifact_tool_forbids_artifact_constructor\n *\n * Flags `new ArtifactTool({ … })` whose options literal sets an `artifactConstructor`.\n *\n * Why: an `ArtifactTool` is the tool that answers queries AGAINST a spooled artifact — it must not\n * itself return a `SpooledArtifact`, or the result would be re-wrapped into another artifact whose\n * query tools are themselves `ArtifactTool`s, and so on without end. The base `RawTool` accepts an\n * `artifactConstructor` (the subclass used to wrap a tool's bytes), but `ArtifactTool` explicitly\n * FORBIDS it (`artifactConstructor: validator.any().forbidden()` in its schema). Supplying one is a\n * recursion footgun the constructor rejects at runtime; this rule surfaces it at the construction\n * site so it fails lint.\n *\n * Opt-out:\n * // eslint-disable-next-line adk/artifact-tool-forbids-artifact-constructor -- <reason>\n */\n\nimport { createRule } 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\nconst isUndefinedValue = (node: TSESTree.Node): boolean =>\n node.type === 'Identifier' && node.name === 'undefined'\n\nconst artifactToolForbidsArtifactConstructorRule = createRule({\n name: 'artifact-tool-forbids-artifact-constructor',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'An `ArtifactTool` must not declare `artifactConstructor` — it answers queries against an artifact and cannot itself return one (infinite re-wrapping). The ArtifactTool schema forbids the field at runtime; this surfaces it at the construction site.',\n },\n schema: [],\n messages: {\n forbidArtifactConstructor:\n '`new ArtifactTool` must not set `artifactConstructor` — an artifact-query tool cannot itself return a SpooledArtifact (it would re-wrap forever). The ArtifactTool schema forbids this field. Remove it. Opt out with an eslint-disable-next-line adk/artifact-tool-forbids-artifact-constructor comment + reason.',\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n NewExpression(node: TSESTree.NewExpression) {\n if (node.callee.type !== 'Identifier' || node.callee.name !== 'ArtifactTool') return\n const arg = node.arguments[0]\n if (!arg || arg.type !== 'ObjectExpression') return\n\n for (const p of arg.properties) {\n if (p.type === 'SpreadElement') continue\n if (literalKey(p) === 'artifactConstructor' && !isUndefinedValue(p.value)) {\n context.report({ node: p, messageId: 'forbidArtifactConstructor' })\n }\n }\n },\n }\n },\n})\n\nexport default artifactToolForbidsArtifactConstructorRule\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAqBA,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;AAEA,IAAM,oBAAoB,SACxB,KAAK,SAAS,gBAAgB,KAAK,SAAS;AAE9C,IAAM,6CAA6C,eAAA,WAAW;CAC5D,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,0PACJ;EACA,QAAQ,CAAC;EACT,UAAU,EACR,2BACE,qTACJ;CACF;CACA,gBAAgB,CAAC;CACjB,OAAO,SAAS;EACd,OAAO,EACL,cAAc,MAA8B;GAC1C,IAAI,KAAK,OAAO,SAAS,gBAAgB,KAAK,OAAO,SAAS,gBAAgB;GAC9E,MAAM,MAAM,KAAK,UAAU;GAC3B,IAAI,CAAC,OAAO,IAAI,SAAS,oBAAoB;GAE7C,KAAK,MAAM,KAAK,IAAI,YAAY;IAC9B,IAAI,EAAE,SAAS,iBAAiB;IAChC,IAAI,WAAW,CAAC,MAAM,yBAAyB,CAAC,iBAAiB,EAAE,KAAK,GACtE,QAAQ,OAAO;KAAE,MAAM;KAAG,WAAW;IAA4B,CAAC;GAEtE;EACF,EACF;CACF;AACF,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @nhtio/adk/eslint/rules/artifact_tool_forbids_artifact_constructor
|
|
3
|
+
*
|
|
4
|
+
* Flags `new ArtifactTool({ … })` whose options literal sets an `artifactConstructor`.
|
|
5
|
+
*
|
|
6
|
+
* Why: an `ArtifactTool` is the tool that answers queries AGAINST a spooled artifact — it must not
|
|
7
|
+
* itself return a `SpooledArtifact`, or the result would be re-wrapped into another artifact whose
|
|
8
|
+
* query tools are themselves `ArtifactTool`s, and so on without end. The base `RawTool` accepts an
|
|
9
|
+
* `artifactConstructor` (the subclass used to wrap a tool's bytes), but `ArtifactTool` explicitly
|
|
10
|
+
* FORBIDS it (`artifactConstructor: validator.any().forbidden()` in its schema). Supplying one is a
|
|
11
|
+
* recursion footgun the constructor rejects at runtime; this rule surfaces it at the construction
|
|
12
|
+
* site so it fails lint.
|
|
13
|
+
*
|
|
14
|
+
* Opt-out:
|
|
15
|
+
* // eslint-disable-next-line adk/artifact-tool-forbids-artifact-constructor -- <reason>
|
|
16
|
+
*/
|
|
17
|
+
declare const artifactToolForbidsArtifactConstructorRule: import("@typescript-eslint/utils/ts-eslint").RuleModule<"forbidArtifactConstructor", [
|
|
18
|
+
], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
19
|
+
name: string;
|
|
20
|
+
};
|
|
21
|
+
export default artifactToolForbidsArtifactConstructorRule;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { t as createRule } from "../../common-BUIjZ6LV.mjs";
|
|
2
|
+
//#region src/eslint/rules/artifact_tool_forbids_artifact_constructor.ts
|
|
3
|
+
/**
|
|
4
|
+
* @module @nhtio/adk/eslint/rules/artifact_tool_forbids_artifact_constructor
|
|
5
|
+
*
|
|
6
|
+
* Flags `new ArtifactTool({ … })` whose options literal sets an `artifactConstructor`.
|
|
7
|
+
*
|
|
8
|
+
* Why: an `ArtifactTool` is the tool that answers queries AGAINST a spooled artifact — it must not
|
|
9
|
+
* itself return a `SpooledArtifact`, or the result would be re-wrapped into another artifact whose
|
|
10
|
+
* query tools are themselves `ArtifactTool`s, and so on without end. The base `RawTool` accepts an
|
|
11
|
+
* `artifactConstructor` (the subclass used to wrap a tool's bytes), but `ArtifactTool` explicitly
|
|
12
|
+
* FORBIDS it (`artifactConstructor: validator.any().forbidden()` in its schema). Supplying one is a
|
|
13
|
+
* recursion footgun the constructor rejects at runtime; this rule surfaces it at the construction
|
|
14
|
+
* site so it fails lint.
|
|
15
|
+
*
|
|
16
|
+
* Opt-out:
|
|
17
|
+
* // eslint-disable-next-line adk/artifact-tool-forbids-artifact-constructor -- <reason>
|
|
18
|
+
*/
|
|
19
|
+
var literalKey = (prop) => {
|
|
20
|
+
if (prop.computed) return void 0;
|
|
21
|
+
if (prop.key.type === "Identifier") return prop.key.name;
|
|
22
|
+
if (prop.key.type === "Literal" && typeof prop.key.value === "string") return prop.key.value;
|
|
23
|
+
};
|
|
24
|
+
var isUndefinedValue = (node) => node.type === "Identifier" && node.name === "undefined";
|
|
25
|
+
var artifactToolForbidsArtifactConstructorRule = createRule({
|
|
26
|
+
name: "artifact-tool-forbids-artifact-constructor",
|
|
27
|
+
meta: {
|
|
28
|
+
type: "problem",
|
|
29
|
+
docs: { description: "An `ArtifactTool` must not declare `artifactConstructor` — it answers queries against an artifact and cannot itself return one (infinite re-wrapping). The ArtifactTool schema forbids the field at runtime; this surfaces it at the construction site." },
|
|
30
|
+
schema: [],
|
|
31
|
+
messages: { forbidArtifactConstructor: "`new ArtifactTool` must not set `artifactConstructor` — an artifact-query tool cannot itself return a SpooledArtifact (it would re-wrap forever). The ArtifactTool schema forbids this field. Remove it. Opt out with an eslint-disable-next-line adk/artifact-tool-forbids-artifact-constructor comment + reason." }
|
|
32
|
+
},
|
|
33
|
+
defaultOptions: [],
|
|
34
|
+
create(context) {
|
|
35
|
+
return { NewExpression(node) {
|
|
36
|
+
if (node.callee.type !== "Identifier" || node.callee.name !== "ArtifactTool") return;
|
|
37
|
+
const arg = node.arguments[0];
|
|
38
|
+
if (!arg || arg.type !== "ObjectExpression") return;
|
|
39
|
+
for (const p of arg.properties) {
|
|
40
|
+
if (p.type === "SpreadElement") continue;
|
|
41
|
+
if (literalKey(p) === "artifactConstructor" && !isUndefinedValue(p.value)) context.report({
|
|
42
|
+
node: p,
|
|
43
|
+
messageId: "forbidArtifactConstructor"
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
} };
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
//#endregion
|
|
50
|
+
export { artifactToolForbidsArtifactConstructorRule as default };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=artifact_tool_forbids_artifact_constructor.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact_tool_forbids_artifact_constructor.mjs","names":[],"sources":["../../../src/eslint/rules/artifact_tool_forbids_artifact_constructor.ts"],"sourcesContent":["/**\n * @module @nhtio/adk/eslint/rules/artifact_tool_forbids_artifact_constructor\n *\n * Flags `new ArtifactTool({ … })` whose options literal sets an `artifactConstructor`.\n *\n * Why: an `ArtifactTool` is the tool that answers queries AGAINST a spooled artifact — it must not\n * itself return a `SpooledArtifact`, or the result would be re-wrapped into another artifact whose\n * query tools are themselves `ArtifactTool`s, and so on without end. The base `RawTool` accepts an\n * `artifactConstructor` (the subclass used to wrap a tool's bytes), but `ArtifactTool` explicitly\n * FORBIDS it (`artifactConstructor: validator.any().forbidden()` in its schema). Supplying one is a\n * recursion footgun the constructor rejects at runtime; this rule surfaces it at the construction\n * site so it fails lint.\n *\n * Opt-out:\n * // eslint-disable-next-line adk/artifact-tool-forbids-artifact-constructor -- <reason>\n */\n\nimport { createRule } 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\nconst isUndefinedValue = (node: TSESTree.Node): boolean =>\n node.type === 'Identifier' && node.name === 'undefined'\n\nconst artifactToolForbidsArtifactConstructorRule = createRule({\n name: 'artifact-tool-forbids-artifact-constructor',\n meta: {\n type: 'problem',\n docs: {\n description:\n 'An `ArtifactTool` must not declare `artifactConstructor` — it answers queries against an artifact and cannot itself return one (infinite re-wrapping). The ArtifactTool schema forbids the field at runtime; this surfaces it at the construction site.',\n },\n schema: [],\n messages: {\n forbidArtifactConstructor:\n '`new ArtifactTool` must not set `artifactConstructor` — an artifact-query tool cannot itself return a SpooledArtifact (it would re-wrap forever). The ArtifactTool schema forbids this field. Remove it. Opt out with an eslint-disable-next-line adk/artifact-tool-forbids-artifact-constructor comment + reason.',\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n NewExpression(node: TSESTree.NewExpression) {\n if (node.callee.type !== 'Identifier' || node.callee.name !== 'ArtifactTool') return\n const arg = node.arguments[0]\n if (!arg || arg.type !== 'ObjectExpression') return\n\n for (const p of arg.properties) {\n if (p.type === 'SpreadElement') continue\n if (literalKey(p) === 'artifactConstructor' && !isUndefinedValue(p.value)) {\n context.report({ node: p, messageId: 'forbidArtifactConstructor' })\n }\n }\n },\n }\n },\n})\n\nexport default artifactToolForbidsArtifactConstructorRule\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,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;AAEA,IAAM,oBAAoB,SACxB,KAAK,SAAS,gBAAgB,KAAK,SAAS;AAE9C,IAAM,6CAA6C,WAAW;CAC5D,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,0PACJ;EACA,QAAQ,CAAC;EACT,UAAU,EACR,2BACE,qTACJ;CACF;CACA,gBAAgB,CAAC;CACjB,OAAO,SAAS;EACd,OAAO,EACL,cAAc,MAA8B;GAC1C,IAAI,KAAK,OAAO,SAAS,gBAAgB,KAAK,OAAO,SAAS,gBAAgB;GAC9E,MAAM,MAAM,KAAK,UAAU;GAC3B,IAAI,CAAC,OAAO,IAAI,SAAS,oBAAoB;GAE7C,KAAK,MAAM,KAAK,IAAI,YAAY;IAC9B,IAAI,EAAE,SAAS,iBAAiB;IAChC,IAAI,WAAW,CAAC,MAAM,yBAAyB,CAAC,iBAAiB,EAAE,KAAK,GACtE,QAAQ,OAAO;KAAE,MAAM;KAAG,WAAW;IAA4B,CAAC;GAEtE;EACF,EACF;CACF;AACF,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `createRule` factory for the published `@nhtio/adk` ESLint rules.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Wraps `ESLintUtils.RuleCreator` so every rule gets a consistent `meta.docs.url` pointing at the
|
|
6
|
+
* published rule reference. Each rule module imports this and default-exports `createRule({...})`.
|
|
7
|
+
*
|
|
8
|
+
* `@typescript-eslint/utils` is an OPTIONAL peer dependency of `@nhtio/adk` — it is only needed by
|
|
9
|
+
* consumers who import `@nhtio/adk/eslint`. The main library never imports this module.
|
|
10
|
+
*/
|
|
11
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
12
|
+
import type { TSESTree } from '@typescript-eslint/utils';
|
|
13
|
+
/**
|
|
14
|
+
* Rule factory for `@nhtio/adk` ESLint rules. The `name` passed to each rule becomes the slug in the
|
|
15
|
+
* generated documentation URL.
|
|
16
|
+
*/
|
|
17
|
+
export declare const createRule: <Options extends readonly unknown[], MessageIds extends string>({ meta, name, ...rule }: Readonly<ESLintUtils.RuleWithMetaAndName<Options, MessageIds, unknown>>) => ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener> & {
|
|
18
|
+
name: string;
|
|
19
|
+
};
|
|
20
|
+
/** Any function-like AST node (arrow, function expression, or declaration). */
|
|
21
|
+
export type FunctionNode = TSESTree.FunctionDeclaration | TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression;
|
|
22
|
+
export declare const isFunctionNode: (node: TSESTree.Node) => node is FunctionNode;
|
|
23
|
+
/**
|
|
24
|
+
* Heuristic: does this call/new expression look like a primary LLM invocation? Detects known
|
|
25
|
+
* provider SDK constructors and `…create()` calls whose receiver chain passes through
|
|
26
|
+
* `chat`/`completions`/`messages`/`responses`, plus a small set of generate* methods. Deliberately
|
|
27
|
+
* conservative — the goal is to catch the obvious footgun, not to be exhaustive.
|
|
28
|
+
*/
|
|
29
|
+
export declare const isLlmCall: (node: TSESTree.Node) => boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Walks the descendants of a function body (NOT crossing into nested function scopes) and invokes
|
|
32
|
+
* `visit` for every node, so a rule can scan a handler/middleware body for a pattern while ignoring
|
|
33
|
+
* inner closures (e.g. a `new TurnRunner` sub-agent's own callbacks).
|
|
34
|
+
*/
|
|
35
|
+
export declare const walkBodySkippingNestedFunctions: (body: TSESTree.Node, visit: (node: TSESTree.Node) => void) => void;
|
|
36
|
+
/**
|
|
37
|
+
* True when the function body runs a scoped sub-agent — either `new TurnRunner(...)` or
|
|
38
|
+
* `DispatchRunner.dispatch(...)` — the documented escape hatch that exempts a tool handler from
|
|
39
|
+
* {@link isLlmCall} flagging.
|
|
40
|
+
*/
|
|
41
|
+
export declare const runsSubAgent: (body: TSESTree.Node) => boolean;
|