mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-01 14:59:19 +08:00
d6e08d38d7
* perf(tools): generate serializable tool metadata artifacts
Adds `scripts/sync-tool-metadata.ts`, which projects the executable tool
registry down to the data half nobody needs a closure for, plus typed accessors
over the result. No consumer is rewired yet — that is the next PR.
`@/tools/registry` is a ~9,000-line barrel over 4,366 tools. Each `ToolConfig`
mixes plain data (`params`, `outputs`, `name`) with closures (`request.headers`,
`transformResponse`, `directExecution`, `postProcess`), and those closures reach
every integration's SDK client and parser — which is why reaching the barrel
costs ~4,700 modules. Every client-reachable caller was audited: none of them
need a closure. They need `outputs`, `params`, or an existence check.
Two artifacts, not one. `outputs` is ~4 MB of the ~8 MB and has a single
consumer, so it is emitted separately and exposed from its own module; callers
needing only params never load it.
The data is a JSON string parsed at runtime rather than an imported `.json` or
an object literal. That is not stylistic — with `resolveJsonModule` (enabled
repo-wide) a `.json` import makes TypeScript infer a literal type for all 4,366
entries:
tsc --noEmit, baseline 12.6s
tsc --noEmit, with `.json` imports 8m07s (38x)
tsc --noEmit, with string literals 12.0s
An ambient `declare module` does not short-circuit it (measured: 8m18s), and an
object literal is the same inference work. A single string literal is one cheap
token for the compiler and the bundler, and `JSON.parse` beats evaluating the
equivalent literal at runtime.
The generator refuses to emit any function value, so shipping executable config
to the client fails loudly instead of silently. `hosting` and `schemaEnrichment`
are excluded on those grounds — both hold functions and are server-only.
Also strips empty param entries: the registry has one (`stt_deepgram_v2`, an
`undefined`) which crashes callers that read `param.type` while iterating.
`JSON.stringify` drops `undefined` on its own, so the guard is there for an
explicit `null` — which serializes faithfully and would reach consumers — and to
warn either way.
Wires `tool-metadata:check` into CI alongside the other generated-contract
gates, and ignores the generated directory in biome (it exceeds the 1 MB limit
and was being skipped with a notice on every commit).
Adds a `tool-registry-boundary` skill covering which module to import, the three
non-obvious properties of the artifacts, and how to verify an edge is actually
cut — the canvas route reaches the registry through four redundant paths, so
cutting one alone moves the module count by ~1.
* fix(tools): harden the metadata accessors against inherited keys
Review found two real defects in the generated-metadata layer.
`JSON.parse` returns an object with the normal prototype, so a bare bracket
lookup resolved inherited members: `getToolMetadata('constructor')` returned a
*function* typed as `ToolMetadata`, and `getToolOutputsMetadata('toString')`
likewise — silently violating the accessors' documented "undefined if unknown"
contract. Guarded with `Object.hasOwn`, with a parameterised regression test
over `constructor`, `toString`, `valueOf`, `hasOwnProperty` and `__proto__`.
The generator's no-functions scan also gave up past ten levels of nesting. Param
and output schemas nest arbitrarily, so a deeper closure would have been dropped
silently by `JSON.stringify` while generation reported success — shipping an
incomplete schema and defeating the guarantee the scan exists to provide. The
depth cap is gone; a `WeakSet` handles the cycles that exposes.
* docs(tools): tell tool authors to regenerate the metadata artifacts
A new tool now has a second registration step. Client code reads `params` and
`outputs` from the generated artifacts rather than from the registry, so a tool
added without regenerating them is registered but invisible to the UI — and CI
fails on the stale artifacts.
`add-tools` and `add-integration` are where someone actually adds a tool, so the
step goes in both, next to the registry edit and in each checklist.
* docs(blocks): note when a block change needs tool-metadata regeneration
Adding a block alone needs no regeneration — it references existing tool IDs and
changes no tool's shape. But a change that touches a tool alongside the block
does, and this is where that is easy to miss: a block's `outputs` are authored
to match its tools' outputs, and the UI now reads those from the generated
metadata, so a stale artifact makes the block's declared outputs disagree with
what the panel renders (and fails CI).
Completes the tool-authoring surface alongside add-tools and add-integration.
* docs(tools): cover tool removal in the regeneration guidance
The three tool-authoring skills said to regenerate after adding or changing a
tool, but not after removing one. Removal is equally breaking and equally
guarded: deleting a tool from `tools/registry.ts` without regenerating fails
`tool-metadata:check` (verified — exit 1), so a contributor following the skill
literally would have hit a CI failure the skill never warned about.
172 lines
4.3 KiB
JSON
172 lines
4.3 KiB
JSON
{
|
|
"$schema": "https://biomejs.dev/schemas/2.0.0-beta.5/schema.json",
|
|
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": false },
|
|
"files": {
|
|
"ignoreUnknown": false,
|
|
"includes": [
|
|
"**",
|
|
"!**/.next",
|
|
"!**/.next",
|
|
"!**/next-env.d.ts",
|
|
"!**/out",
|
|
"!**/dist",
|
|
"!**/build",
|
|
"!**/node_modules",
|
|
"!**/.bun",
|
|
"!**/.cache",
|
|
"!**/.turbo",
|
|
"!**/.DS_Store",
|
|
"!**/*.pem",
|
|
"!**/bun-debug.log*",
|
|
"!**/.env*.local",
|
|
"!**/.env",
|
|
"!**/.vercel",
|
|
"!**/coverage",
|
|
"!**/public/sw.js",
|
|
"!**/public/workbox-*.js",
|
|
"!**/public/worker-*.js",
|
|
"!**/public/fallback-*.js",
|
|
"!**/apps/sim/tools/generated",
|
|
"!**/apps/docs/.source",
|
|
"!**/apps/desktop/release",
|
|
"!**/venv",
|
|
"!**/.venv",
|
|
"!**/uploads",
|
|
"!**/apps/sim/lib/execution/sandbox/bundles/*.cjs",
|
|
"!**/test-results",
|
|
"!**/playwright-report"
|
|
]
|
|
},
|
|
"formatter": {
|
|
"enabled": true,
|
|
"useEditorconfig": true,
|
|
"formatWithErrors": false,
|
|
"indentStyle": "space",
|
|
"indentWidth": 2,
|
|
"lineEnding": "lf",
|
|
"lineWidth": 100,
|
|
"attributePosition": "auto",
|
|
"bracketSpacing": true
|
|
},
|
|
"assist": {
|
|
"actions": {
|
|
"source": {
|
|
"organizeImports": {
|
|
"level": "on",
|
|
"options": {
|
|
"groups": [
|
|
[":NODE:", "react", "react/**"],
|
|
":PACKAGE:",
|
|
"@/components/**",
|
|
"@/lib/**",
|
|
"@/app/**",
|
|
":ALIAS:",
|
|
":RELATIVE:"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"linter": {
|
|
"enabled": true,
|
|
"rules": {
|
|
"recommended": true,
|
|
"nursery": {
|
|
"useSortedClasses": "warn"
|
|
},
|
|
"a11y": {
|
|
"noSvgWithoutTitle": "off",
|
|
"useAltText": "off",
|
|
"useKeyWithClickEvents": "off",
|
|
"noRedundantAlt": "off",
|
|
"useSemanticElements": "off",
|
|
"useButtonType": "off",
|
|
"useFocusableInteractive": "off",
|
|
"noStaticElementInteractions": "off",
|
|
"useAriaPropsSupportedByRole": "off",
|
|
"useAriaPropsForRole": "off"
|
|
},
|
|
"suspicious": {
|
|
"noImplicitAnyLet": "off",
|
|
"noArrayIndexKey": "off",
|
|
"noExplicitAny": "off",
|
|
"noControlCharactersInRegex": "off",
|
|
"noThenProperty": "off",
|
|
"noAssignInExpressions": "off",
|
|
"noDocumentCookie": "off"
|
|
},
|
|
"correctness": {
|
|
"useExhaustiveDependencies": "off",
|
|
"noUnusedFunctionParameters": "off",
|
|
"noUnusedVariables": "off"
|
|
},
|
|
"security": {
|
|
"noDangerouslySetInnerHtml": "off"
|
|
},
|
|
"style": {
|
|
"noDescendingSpecificity": "off",
|
|
"noNonNullAssertion": "off",
|
|
"noParameterAssign": "off",
|
|
"useNodejsImportProtocol": "off",
|
|
"useAsConstAssertion": "error",
|
|
"useDefaultParameterLast": "error",
|
|
"useEnumInitializers": "error",
|
|
"useSelfClosingElements": "error",
|
|
"useSingleVarDeclarator": "error",
|
|
"noUnusedTemplateLiteral": "off",
|
|
"useNumberNamespace": "error",
|
|
"noInferrableTypes": "error",
|
|
"noUselessElse": "error",
|
|
"noRestrictedImports": {
|
|
"level": "error",
|
|
"options": {
|
|
"paths": {
|
|
"nanoid": "Use generateId() or generateShortId() from @sim/utils/id instead",
|
|
"uuid": "Use generateId() from @sim/utils/id instead"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"complexity": {
|
|
"noForEach": "off",
|
|
"noUselessFragments": "off",
|
|
"noStaticOnlyClass": "off"
|
|
},
|
|
"performance": {
|
|
"noAccumulatingSpread": "off",
|
|
"noDelete": "error",
|
|
"noImgElement": "off"
|
|
}
|
|
}
|
|
},
|
|
"javascript": {
|
|
"formatter": {
|
|
"jsxQuoteStyle": "single",
|
|
"quoteProperties": "asNeeded",
|
|
"trailingCommas": "es5",
|
|
"semicolons": "asNeeded",
|
|
"arrowParentheses": "always",
|
|
"bracketSameLine": false,
|
|
"quoteStyle": "single",
|
|
"attributePosition": "auto",
|
|
"bracketSpacing": true
|
|
}
|
|
},
|
|
"css": {
|
|
"parser": {
|
|
"cssModules": true
|
|
},
|
|
"formatter": {
|
|
"enabled": true,
|
|
"indentWidth": 2
|
|
}
|
|
},
|
|
"json": {
|
|
"formatter": {
|
|
"enabled": true,
|
|
"indentWidth": 2
|
|
}
|
|
}
|
|
}
|