diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 6d2204373..8dfba6fd6 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -4,7 +4,7 @@ module.exports = {
     es2021: true,
   },
   root: true,
-  extends: ['standard-with-typescript', 'plugin:react/recommended'],
+  extends: ['standard-with-typescript'],
   plugins: ['import', '@typescript-eslint'],
   overrides: [
     {
@@ -52,7 +52,8 @@ module.exports = {
     'prefer-const': 'error',
     'eol-last': 'off',
     'import/no-duplicates': 'error',
-    'import/no-cycle': 'error',
+    // TODO: Enable after fixing cycle in CallGraphNode -> globalRivetNodeRegistry
+    'import/no-cycle': 'warn',
     'no-extra-boolean-cast': 'off',
     'no-prototype-builtins': 'off',
     'no-undef-init': 'off',
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index cd6720165..815ebb9ab 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -23,3 +23,7 @@ jobs:
           NODE_OPTIONS: --max_old_space_size=6000
       - name: Test
         run: yarn test
+      - name: Lint
+        run: yarn lint
+      - name: Prettier
+        run: yarn prettier --check
diff --git a/.pnp.cjs b/.pnp.cjs
index d1c32b985..89de913be 100755
--- a/.pnp.cjs
+++ b/.pnp.cjs
@@ -6730,8 +6730,9 @@ const RAW_RUNTIME_STATE =
           ["@ironclad/rivet-cli", "workspace:packages/cli"],\
           ["@ironclad/rivet-node", "workspace:packages/node"],\
           ["@types/yargs", "npm:17.0.29"],\
+          ["@typescript-eslint/eslint-plugin", "virtual:388c29633752d7c364e0487c276ae72861ce5d69c069bff16a49b35801303d87d39cb24723bbac1721c48df59f346575324fe3c6de8ead4fb7d83d6ae4a0e521#npm:6.9.0"],\
           ["eslint", "npm:8.52.0"],\
-          ["eslint-config-standard-with-typescript", "virtual:bbd91125264a56425cb84647dbdb3897ebf3aa2cdf8c02bff68076c228ddd912fb3bb6659c03fbe9e3e3898d8847f372ce79bb07d1395c306ae5ef774f9a7fa3#npm:39.1.1"],\
+          ["eslint-config-standard-with-typescript", "virtual:388c29633752d7c364e0487c276ae72861ce5d69c069bff16a49b35801303d87d39cb24723bbac1721c48df59f346575324fe3c6de8ead4fb7d83d6ae4a0e521#npm:39.1.1"],\
           ["eslint-plugin-import", "virtual:388c29633752d7c364e0487c276ae72861ce5d69c069bff16a49b35801303d87d39cb24723bbac1721c48df59f346575324fe3c6de8ead4fb7d83d6ae4a0e521#npm:2.29.0"],\
           ["eslint-plugin-n", "virtual:388c29633752d7c364e0487c276ae72861ce5d69c069bff16a49b35801303d87d39cb24723bbac1721c48df59f346575324fe3c6de8ead4fb7d83d6ae4a0e521#npm:16.2.0"],\
           ["eslint-plugin-promise", "virtual:388c29633752d7c364e0487c276ae72861ce5d69c069bff16a49b35801303d87d39cb24723bbac1721c48df59f346575324fe3c6de8ead4fb7d83d6ae4a0e521#npm:6.1.1"],\
@@ -14719,41 +14720,6 @@ const RAW_RUNTIME_STATE =
         ],\
         "linkType": "HARD"\
       }],\
-      ["virtual:bbd91125264a56425cb84647dbdb3897ebf3aa2cdf8c02bff68076c228ddd912fb3bb6659c03fbe9e3e3898d8847f372ce79bb07d1395c306ae5ef774f9a7fa3#npm:39.1.1", {\
-        "packageLocation": "./.yarn/__virtual__/eslint-config-standard-with-typescript-virtual-dbb4c907a2/0/cache/eslint-config-standard-with-typescript-npm-39.1.1-ff5efff6e8-75b5e29b1e.zip/node_modules/eslint-config-standard-with-typescript/",\
-        "packageDependencies": [\
-          ["eslint-config-standard-with-typescript", "virtual:bbd91125264a56425cb84647dbdb3897ebf3aa2cdf8c02bff68076c228ddd912fb3bb6659c03fbe9e3e3898d8847f372ce79bb07d1395c306ae5ef774f9a7fa3#npm:39.1.1"],\
-          ["@types/eslint", null],\
-          ["@types/eslint-plugin-import", null],\
-          ["@types/eslint-plugin-n", null],\
-          ["@types/eslint-plugin-promise", null],\
-          ["@types/typescript", null],\
-          ["@types/typescript-eslint__eslint-plugin", null],\
-          ["@typescript-eslint/eslint-plugin", null],\
-          ["@typescript-eslint/parser", "virtual:ce0a1a3dc1d2c68486e45f658daa2797aed508b6c7f8165837d04e87640f4a86cc4ae6e30f0913571707a77e538d32fd97db17859e4991b7ed430142d73bec3b#npm:6.9.1"],\
-          ["eslint", "npm:8.52.0"],\
-          ["eslint-config-standard", "virtual:ce0a1a3dc1d2c68486e45f658daa2797aed508b6c7f8165837d04e87640f4a86cc4ae6e30f0913571707a77e538d32fd97db17859e4991b7ed430142d73bec3b#npm:17.1.0"],\
-          ["eslint-plugin-import", "virtual:388c29633752d7c364e0487c276ae72861ce5d69c069bff16a49b35801303d87d39cb24723bbac1721c48df59f346575324fe3c6de8ead4fb7d83d6ae4a0e521#npm:2.29.0"],\
-          ["eslint-plugin-n", "virtual:388c29633752d7c364e0487c276ae72861ce5d69c069bff16a49b35801303d87d39cb24723bbac1721c48df59f346575324fe3c6de8ead4fb7d83d6ae4a0e521#npm:16.2.0"],\
-          ["eslint-plugin-promise", "virtual:388c29633752d7c364e0487c276ae72861ce5d69c069bff16a49b35801303d87d39cb24723bbac1721c48df59f346575324fe3c6de8ead4fb7d83d6ae4a0e521#npm:6.1.1"],\
-          ["typescript", "patch:typescript@npm%3A5.2.2#optional!builtin<compat/typescript>::version=5.2.2&hash=f3b441"]\
-        ],\
-        "packagePeers": [\
-          "@types/eslint-plugin-import",\
-          "@types/eslint-plugin-n",\
-          "@types/eslint-plugin-promise",\
-          "@types/eslint",\
-          "@types/typescript-eslint__eslint-plugin",\
-          "@types/typescript",\
-          "@typescript-eslint/eslint-plugin",\
-          "eslint-plugin-import",\
-          "eslint-plugin-n",\
-          "eslint-plugin-promise",\
-          "eslint",\
-          "typescript"\
-        ],\
-        "linkType": "HARD"\
-      }],\
       ["virtual:f9fb339f6b948da33dabfb081667f976e95ec9e5a804a6ae723484b48758a3c6b4ae83d894110dcf0bece688b8e216d48353a446d7ad8694a8b19291cba8d0a8#npm:39.1.1", {\
         "packageLocation": "./.yarn/__virtual__/eslint-config-standard-with-typescript-virtual-bc256cc5b9/0/cache/eslint-config-standard-with-typescript-npm-39.1.1-ff5efff6e8-75b5e29b1e.zip/node_modules/eslint-config-standard-with-typescript/",\
         "packageDependencies": [\
diff --git a/packages/app/src/components/Port.tsx b/packages/app/src/components/Port.tsx
index 3ac6255d5..6c3e2cd81 100644
--- a/packages/app/src/components/Port.tsx
+++ b/packages/app/src/components/Port.tsx
@@ -116,7 +116,11 @@ export const Port: FC<{
         >
           {canDragTo && <div className={clsx('port-hover-area')} />}
         </div>
-        <div className={clsx("port-label", preservePortCase ? "" : "port-label-uppercase")} onMouseOver={handleMouseOver} onMouseOut={handleMouseOut}>
+        <div
+          className={clsx('port-label', preservePortCase ? '' : 'port-label-uppercase')}
+          onMouseOver={handleMouseOver}
+          onMouseOut={handleMouseOut}
+        >
           {title}
         </div>
       </div>
diff --git a/packages/cli/.eslintrc.cjs b/packages/cli/.eslintrc.cjs
index 8afe9c62d..252b10505 100644
--- a/packages/cli/.eslintrc.cjs
+++ b/packages/cli/.eslintrc.cjs
@@ -5,7 +5,7 @@ module.exports = {
     {
       files: ['*.ts', '*.tsx'],
       parserOptions: {
-        project: './packages/cli/tsconfig.json',
+        project: './tsconfig.json',
         ecmaVersion: 'latest',
         sourceType: 'module',
       },
diff --git a/packages/cli/bin/cli.js b/packages/cli/bin/cli.js
index e207f1811..1b6ff6dd1 100644
--- a/packages/cli/bin/cli.js
+++ b/packages/cli/bin/cli.js
@@ -3,99 +3,108 @@ import { resolve } from 'node:path';
 import yargs from 'yargs';
 import { hideBin } from 'yargs/helpers';
 await yargs(hideBin(process.argv))
-    .command('run <projectFile> [graphName]', 'Run a graph in a project file, or the main graph if graphName is not specified.', (y) => y
-    .positional('projectFile', {
-    describe: 'The project file to run',
-    type: 'string',
-    demandOption: true,
-})
-    .positional('graphName', {
-    describe: 'The name of the graph to run',
-    type: 'string',
-})
-    .option('inputs-stdin', {
-    describe: 'Read inputs from stdin as JSON',
-    type: 'boolean',
-    default: false,
-})
-    .option('include-cost', {
-    describe: 'Include the total cost in the output',
-    type: 'boolean',
-    default: false,
-})
-    .option('context', {
-    describe: 'Adds a context value to the graph run',
-    type: 'string',
-    array: true,
-    default: [],
-})
-    .option('input', {
-    describe: 'Adds an input to the graph run',
-    type: 'string',
-    array: true,
-    default: [],
-}), (args) => run(args))
-    .demandCommand()
-    .parseAsync();
+  .command(
+    'run <projectFile> [graphName]',
+    'Run a graph in a project file, or the main graph if graphName is not specified.',
+    (y) =>
+      y
+        .positional('projectFile', {
+          describe: 'The project file to run',
+          type: 'string',
+          demandOption: true,
+        })
+        .positional('graphName', {
+          describe: 'The name of the graph to run',
+          type: 'string',
+        })
+        .option('inputs-stdin', {
+          describe: 'Read inputs from stdin as JSON',
+          type: 'boolean',
+          default: false,
+        })
+        .option('include-cost', {
+          describe: 'Include the total cost in the output',
+          type: 'boolean',
+          default: false,
+        })
+        .option('context', {
+          describe: 'Adds a context value to the graph run',
+          type: 'string',
+          array: true,
+          default: [],
+        })
+        .option('input', {
+          describe: 'Adds an input to the graph run',
+          type: 'string',
+          array: true,
+          default: [],
+        }),
+    (args) => run(args),
+  )
+  .demandCommand()
+  .parseAsync();
 async function run(args) {
-    try {
-        const projectPath = resolve(process.cwd(), args.projectFile);
-        const project = await loadProjectFromFile(projectPath);
-        if (!args.graphName && !project.metadata.mainGraphId) {
-            const validGraphs = Object.values(project.graphs).map((graph) => [graph.metadata.id, graph.metadata.name]);
-            const validGraphNames = validGraphs.map(([id, name]) => `• "${name}" (${id})`);
-            console.error(`No graph name provided, and project does not specify a main graph. Valid graphs are: \n${validGraphNames.join('\n')}\n\n Use either the graph's name or its ID. For example, \`rivet run my-project.rivet-project my-graph\` or \`rivet run my-project.rivet-project 1234abcd\``);
-            process.exit(1);
-        }
-        let inputs = {};
-        if (args.inputsStdin) {
-            // Read json from stdin
-            const stdin = process.stdin;
-            stdin.setEncoding('utf8');
-            let input = '';
-            for await (const chunk of stdin) {
-                input += chunk;
-            }
-            try {
-                inputs = JSON.parse(input);
-            }
-            catch (err) {
-                console.error('Failed to parse input JSON');
-                console.error(err);
-                process.exit(1);
-            }
-        }
-        else {
-            inputs = Object.fromEntries(args.input.map((input) => {
-                const [key, value] = input.split('=');
-                if (!key || !value) {
-                    console.error(`Invalid input value: ${input}`);
-                    process.exit(1);
-                }
-                return [key, value];
-            }));
-        }
-        const contextValues = Object.fromEntries(args.context.map((context) => {
-            const [key, value] = context.split('=');
-            if (!key || !value) {
-                console.error(`Invalid context value: ${context}`);
-                process.exit(1);
-            }
-            return [key, value];
-        }));
-        const { run } = createProcessor(project, {
-            graph: args.graphName,
-            inputs,
-            context: contextValues,
-        });
-        const outputs = await run();
-        if (!args.includeCost) {
-            delete outputs.cost;
-        }
-        console.log(outputs);
+  try {
+    const projectPath = resolve(process.cwd(), args.projectFile);
+    const project = await loadProjectFromFile(projectPath);
+    if (!args.graphName && !project.metadata.mainGraphId) {
+      const validGraphs = Object.values(project.graphs).map((graph) => [graph.metadata.id, graph.metadata.name]);
+      const validGraphNames = validGraphs.map(([id, name]) => `• "${name}" (${id})`);
+      console.error(
+        `No graph name provided, and project does not specify a main graph. Valid graphs are: \n${validGraphNames.join('\n')}\n\n Use either the graph's name or its ID. For example, \`rivet run my-project.rivet-project my-graph\` or \`rivet run my-project.rivet-project 1234abcd\``,
+      );
+      process.exit(1);
     }
-    catch (err) {
+    let inputs = {};
+    if (args.inputsStdin) {
+      // Read json from stdin
+      const stdin = process.stdin;
+      stdin.setEncoding('utf8');
+      let input = '';
+      for await (const chunk of stdin) {
+        input += chunk;
+      }
+      try {
+        inputs = JSON.parse(input);
+      } catch (err) {
+        console.error('Failed to parse input JSON');
         console.error(err);
         process.exit(1);
+      }
+    } else {
+      inputs = Object.fromEntries(
+        args.input.map((input) => {
+          const [key, value] = input.split('=');
+          if (!key || !value) {
+            console.error(`Invalid input value: ${input}`);
+            process.exit(1);
+          }
+          return [key, value];
+        }),
+      );
+    }
+    const contextValues = Object.fromEntries(
+      args.context.map((context) => {
+        const [key, value] = context.split('=');
+        if (!key || !value) {
+          console.error(`Invalid context value: ${context}`);
+          process.exit(1);
+        }
+        return [key, value];
+      }),
+    );
+    const { run } = createProcessor(project, {
+      graph: args.graphName,
+      inputs,
+      context: contextValues,
+    });
+    const outputs = await run();
+    if (!args.includeCost) {
+      delete outputs.cost;
     }
+    console.log(outputs);
+  } catch (err) {
+    console.error(err);
+    process.exit(1);
+  }
 }
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 58daa375c..ed3568ea3 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -26,6 +26,7 @@
   },
   "devDependencies": {
     "@types/yargs": "^17.0.29",
+    "@typescript-eslint/eslint-plugin": "^6.9.0",
     "eslint": "^8.52.0",
     "eslint-config-standard-with-typescript": "^39.1.1",
     "eslint-plugin-import": "^2.29.0",
diff --git a/packages/core/src/integrations/openai/OpenAIEmbeddingGenerator.ts b/packages/core/src/integrations/openai/OpenAIEmbeddingGenerator.ts
index 1e5c1e319..6989ec521 100644
--- a/packages/core/src/integrations/openai/OpenAIEmbeddingGenerator.ts
+++ b/packages/core/src/integrations/openai/OpenAIEmbeddingGenerator.ts
@@ -2,7 +2,7 @@ import { type Settings } from '../../index.js';
 import { type EmbeddingGenerator } from '../EmbeddingGenerator.js';
 import { OpenAI } from 'openai';
 
-type OpenAIOptions = Pick<OpenAI.EmbeddingCreateParams, 'model' | 'dimensions' >
+type OpenAIOptions = Pick<OpenAI.EmbeddingCreateParams, 'model' | 'dimensions'>;
 
 export class OpenAIEmbeddingGenerator implements EmbeddingGenerator {
   readonly #settings;
@@ -21,7 +21,7 @@ export class OpenAIEmbeddingGenerator implements EmbeddingGenerator {
     const response = await api.embeddings.create({
       input: text,
       model: options?.model ?? 'text-embedding-ada-002',
-      dimensions: options?.dimensions
+      dimensions: options?.dimensions,
     });
 
     const embeddings = response.data;
diff --git a/packages/core/src/model/GraphProcessor.ts b/packages/core/src/model/GraphProcessor.ts
index 014070b0d..2c70c6459 100644
--- a/packages/core/src/model/GraphProcessor.ts
+++ b/packages/core/src/model/GraphProcessor.ts
@@ -919,7 +919,7 @@ export class GraphProcessor {
     if (this.runToNodeIds) {
       const dependencyNodes = this.getDependencyNodesDeep(node.id);
 
-      if (this.runToNodeIds.some((runTo) => runTo != node.id && dependencyNodes.includes(runTo))) {
+      if (this.runToNodeIds.some((runTo) => runTo !== node.id && dependencyNodes.includes(runTo))) {
         this.#emitter.emit('trace', `Node ${node.title} is excluded due to runToNodeIds`);
         return;
       }
diff --git a/packages/core/src/model/Nodes.ts b/packages/core/src/model/Nodes.ts
index 4e2993b66..821fc56f5 100644
--- a/packages/core/src/model/Nodes.ts
+++ b/packages/core/src/model/Nodes.ts
@@ -1,6 +1,4 @@
-import type { ChartNode } from './NodeBase.js';
 import { NodeRegistration } from './NodeRegistration.js';
-import type { NodeImpl } from './NodeImpl.js';
 
 import { userInputNode } from './nodes/UserInputNode.js';
 export * from './nodes/UserInputNode.js';
@@ -218,8 +216,6 @@ export * from './nodes/DelegateFunctionCallNode.js';
 import { playAudioNode } from './nodes/PlayAudioNode.js';
 export * from './nodes/PlayAudioNode.js';
 
-export * from './nodes/CallGraphNode.js';
-
 export const registerBuiltInNodes = (registry: NodeRegistration) => {
   return registry
     .register(toYamlNode)
diff --git a/packages/core/src/model/nodes/CallGraphNode.ts b/packages/core/src/model/nodes/CallGraphNode.ts
index f5a5ed381..4e9f53ff1 100644
--- a/packages/core/src/model/nodes/CallGraphNode.ts
+++ b/packages/core/src/model/nodes/CallGraphNode.ts
@@ -8,12 +8,11 @@ import {
 import { NodeImpl, type NodeUIData } from '../NodeImpl.js';
 import { nodeDefinition } from '../NodeDefinition.js';
 import { type Inputs, type Outputs } from '../GraphProcessor.js';
-import { type GraphId } from '../NodeGraph.js';
 import { nanoid } from 'nanoid/non-secure';
 import { type InternalProcessContext } from '../ProcessContext.js';
 import { dedent } from 'ts-dedent';
-import { coerceType, coerceTypeOptional } from '../../utils/coerceType.js';
-import { looseDataValuesToDataValues, type LooseDataValue } from '../../index.js';
+import { coerceTypeOptional } from '../../utils/coerceType.js';
+import { looseDataValuesToDataValues, type LooseDataValue } from '../../api/createProcessor.js';
 import { getError } from '../../utils/errors.js';
 
 export type CallGraphNode = ChartNode<'callGraph', CallGraphNodeData>;
@@ -122,7 +121,7 @@ export class CallGraphNodeImpl extends NodeImpl<CallGraphNode> {
 
     const subGraphProcessor = context.createSubProcessor(graphRef.graphId, { signal: context.signal });
 
-    let outputs: Outputs = {};
+    const outputs: Outputs = {};
 
     try {
       const startTime = Date.now();
diff --git a/packages/core/src/model/nodes/ExtractJsonNode.ts b/packages/core/src/model/nodes/ExtractJsonNode.ts
index d14a0a590..0abbbed8e 100644
--- a/packages/core/src/model/nodes/ExtractJsonNode.ts
+++ b/packages/core/src/model/nodes/ExtractJsonNode.ts
@@ -94,10 +94,10 @@ export class ExtractJsonNodeImpl extends NodeImpl<ExtractJsonNode> {
 
     // Find the first { or [ and the last } or ], and try parsing everything in between including them.
 
-    let firstBracket = inputString.indexOf('{');
-    let lastBracket = inputString.lastIndexOf('}');
-    let firstSquareBracket = inputString.indexOf('[');
-    let lastSquareBracket = inputString.lastIndexOf(']');
+    const firstBracket = inputString.indexOf('{');
+    const lastBracket = inputString.lastIndexOf('}');
+    const firstSquareBracket = inputString.indexOf('[');
+    const lastSquareBracket = inputString.lastIndexOf(']');
 
     const firstIndex =
       firstBracket >= 0 && firstSquareBracket >= 0
diff --git a/packages/core/src/model/nodes/GetEmbeddingNode.ts b/packages/core/src/model/nodes/GetEmbeddingNode.ts
index ac4e1ec1d..308bf05f5 100644
--- a/packages/core/src/model/nodes/GetEmbeddingNode.ts
+++ b/packages/core/src/model/nodes/GetEmbeddingNode.ts
@@ -37,7 +37,7 @@ export class GetEmbeddingNodeImpl extends NodeImpl<GetEmbeddingNode> {
         integration: 'openai',
         useIntegrationInput: false,
         model: undefined,
-        dimensions: undefined
+        dimensions: undefined,
       },
     };
   }
@@ -141,7 +141,7 @@ export class GetEmbeddingNodeImpl extends NodeImpl<GetEmbeddingNode> {
     const integrationName = this.data.useIntegrationInput
       ? coerceType(inputs['integration' as PortId], 'string')
       : this.data.integration;
-    
+
     const model = this.data.useModelInput ? coerceType(inputs['model' as PortId], 'string') : this.data.model;
 
     const dimensions = this.data.useDimensionsInput
diff --git a/packages/core/src/model/nodes/HttpCallNode.ts b/packages/core/src/model/nodes/HttpCallNode.ts
index 9fe70d87a..272b63288 100644
--- a/packages/core/src/model/nodes/HttpCallNode.ts
+++ b/packages/core/src/model/nodes/HttpCallNode.ts
@@ -211,7 +211,9 @@ export class HttpCallNodeImpl extends NodeImpl<HttpCallNode> {
     const method = getInputOrData(this.data, inputs, 'method', 'string');
     const url = getInputOrData(this.data, inputs, 'url', 'string');
 
+    // TODO: Use URL.canParse when we drop support for Node 18
     try {
+      // eslint-disable-next-line no-new
       new URL(url);
     } catch (err) {
       throw new Error(`Invalid URL: ${url}`);
diff --git a/packages/core/src/plugins/anthropic/nodes/ChatAnthropicNode.ts b/packages/core/src/plugins/anthropic/nodes/ChatAnthropicNode.ts
index 63a36ce1b..7a6f1dcaf 100644
--- a/packages/core/src/plugins/anthropic/nodes/ChatAnthropicNode.ts
+++ b/packages/core/src/plugins/anthropic/nodes/ChatAnthropicNode.ts
@@ -1,5 +1,4 @@
 import {
-  uint8ArrayToBase64,
   type ChartNode,
   type ChatMessage,
   type EditorDefinition,
@@ -42,6 +41,7 @@ import { getScalarTypeOf, isArrayDataValue } from '../../../model/DataValue.js';
 import type { TokenizerCallInfo } from '../../../integrations/Tokenizer.js';
 import { assertNever } from '../../../utils/assertNever.js';
 import { isNotNull } from '../../../utils/genericUtilFunctions.js';
+import { uint8ArrayToBase64 } from '../../../utils/base64.js';
 
 export type ChatAnthropicNode = ChartNode<'chatAnthropic', ChatAnthropicNodeData>;
 
@@ -420,7 +420,7 @@ export const ChatAnthropicNodeImpl: PluginNodeImpl<ChatAnthropicNode> = {
             top_p: useTopP ? topP : undefined,
             max_tokens: maxTokens ?? modelInfo.maxTokens,
             stop_sequences: stop ? [stop] : undefined,
-            system: system,
+            system,
             messages,
             tools: tools
               ? tools.map((tool) => ({ name: tool.name, description: tool.description, input_schema: tool.parameters }))
@@ -507,8 +507,8 @@ export const ChatAnthropicNodeImpl: PluginNodeImpl<ChatAnthropicNode> = {
 
             // Process the response chunks and update the output
             const responseParts: string[] = [];
-            let requestTokens: number | undefined = undefined,
-              responseTokens: number | undefined = undefined;
+            let requestTokens: number | undefined = undefined;
+            let responseTokens: number | undefined = undefined;
             for await (const chunk of chunks) {
               let completion: string = '';
               if (chunk.type === 'content_block_start') {
diff --git a/packages/core/src/plugins/google/google.ts b/packages/core/src/plugins/google/google.ts
index 20b31fa09..e8cd35e35 100644
--- a/packages/core/src/plugins/google/google.ts
+++ b/packages/core/src/plugins/google/google.ts
@@ -74,6 +74,7 @@ export type ChatCompletionChunk = {
   model: string;
 };
 
+/* eslint-disable @typescript-eslint/naming-convention */
 export async function* streamChatCompletions({
   project,
   location,
@@ -92,7 +93,7 @@ export async function* streamChatCompletions({
   const { VertexAI } = await import('@google-cloud/vertexai');
 
   // Can't find a way to pass the credentials path in
-  process.env['GOOGLE_APPLICATION_CREDENTIALS'] = applicationCredentials;
+  process.env.GOOGLE_APPLICATION_CREDENTIALS = applicationCredentials;
   const vertexAi = new VertexAI({ project, location });
   const generativeModel = vertexAi.preview.getGenerativeModel({
     model,
diff --git a/packages/core/src/plugins/google/nodes/ChatGoogleNode.ts b/packages/core/src/plugins/google/nodes/ChatGoogleNode.ts
index 2ba1a9c13..f26855017 100644
--- a/packages/core/src/plugins/google/nodes/ChatGoogleNode.ts
+++ b/packages/core/src/plugins/google/nodes/ChatGoogleNode.ts
@@ -1,5 +1,4 @@
 import {
-  uint8ArrayToBase64,
   type ChartNode,
   type ChatMessage,
   type EditorDefinition,
@@ -29,6 +28,7 @@ import { match } from 'ts-pattern';
 import { coerceType, coerceTypeOptional } from '../../../utils/coerceType.js';
 import { addWarning } from '../../../utils/outputs.js';
 import { getError } from '../../../utils/errors.js';
+import { uint8ArrayToBase64 } from '../../../utils/base64.js';
 import { pluginNodeDefinition } from '../../../model/NodeDefinition.js';
 import { getScalarTypeOf, isArrayDataValue } from '../../../model/DataValue.js';
 import type { TokenizerCallInfo } from '../../../integrations/Tokenizer.js';
diff --git a/packages/core/src/plugins/openai/nodes/RunThreadNode.ts b/packages/core/src/plugins/openai/nodes/RunThreadNode.ts
index 29ceb5c09..b3e06cc61 100644
--- a/packages/core/src/plugins/openai/nodes/RunThreadNode.ts
+++ b/packages/core/src/plugins/openai/nodes/RunThreadNode.ts
@@ -8,10 +8,6 @@ import {
   type GptFunction,
   type GraphId,
   type GraphInputs,
-  type ScalarDataValue,
-  isArrayDataValue,
-  arrayizeDataValue,
-  unwrapDataValue,
 } from '../../../index.js';
 import {
   openAiModelOptions,
@@ -25,10 +21,10 @@ import {
   type OpenAIThreadMessage,
 } from '../../../utils/openai.js';
 import { dedent, newId, coerceTypeOptional, getInputOrData } from '../../../utils/index.js';
+import { arrayizeDataValue, unwrapDataValue } from '../../../model/DataValue.js';
 import { pluginNodeDefinition } from '../../../model/NodeDefinition.js';
 import { handleOpenAIError } from '../handleOpenaiError.js';
 import { type DataValue } from '../../../model/DataValue.js';
-import { match } from 'ts-pattern';
 
 export type RunThreadNode = ChartNode<'openaiRunThread', RunThreadNodeData>;
 
diff --git a/packages/core/src/plugins/pinecone/PineconeVectorDatabase.ts b/packages/core/src/plugins/pinecone/PineconeVectorDatabase.ts
index cd3d382bf..a2bc57623 100644
--- a/packages/core/src/plugins/pinecone/PineconeVectorDatabase.ts
+++ b/packages/core/src/plugins/pinecone/PineconeVectorDatabase.ts
@@ -23,11 +23,10 @@ export class PineconeVectorDatabase implements VectorDatabase {
       id = CryptoJS.SHA256(vector.value.join(',')).toString(CryptoJS.enc.Hex);
     }
 
-    let metadata: Record<string, unknown> = {}
+    let metadata: Record<string, unknown> = {};
     if (data.type === 'object') {
       metadata = data.value;
-    }
-    else {
+    } else {
       metadata = { data: data.value };
     }
 
diff --git a/yarn.lock b/yarn.lock
index 65d1d7659..3784309b9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3882,6 +3882,7 @@ __metadata:
   dependencies:
     "@ironclad/rivet-node": "workspace:^"
     "@types/yargs": "npm:^17.0.29"
+    "@typescript-eslint/eslint-plugin": "npm:^6.9.0"
     eslint: "npm:^8.52.0"
     eslint-config-standard-with-typescript: "npm:^39.1.1"
     eslint-plugin-import: "npm:^2.29.0"