diff --git a/src/Netscript/WorkerScript.ts b/src/Netscript/WorkerScript.ts
index c8c9515d3..3ef23d884 100644
--- a/src/Netscript/WorkerScript.ts
+++ b/src/Netscript/WorkerScript.ts
@@ -94,11 +94,6 @@ export class WorkerScript {
    */
   ramUsage = 0;
 
-  /**
-   * Whether or not this workerScript is currently running
-   */
-  running = false;
-
   /**
    * Reference to underlying RunningScript object
    */
diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts
index e1a528e0b..e9ab76745 100644
--- a/src/NetscriptFunctions.ts
+++ b/src/NetscriptFunctions.ts
@@ -702,7 +702,7 @@ const base: InternalAPI<NS> = {
         throw helpers.makeRuntimeErrorMsg(ctx, "Could not find server. This is a bug. Report to dev.");
       }
 
-      return runScriptFromScript(Player, "run", scriptServer, scriptname, args, ctx.workerScript, threads);
+      return runScriptFromScript("run", scriptServer, scriptname, args, ctx.workerScript, threads);
     },
   exec:
     (ctx: NetscriptContext) =>
@@ -718,7 +718,7 @@ const base: InternalAPI<NS> = {
         throw helpers.makeRuntimeErrorMsg(ctx, `Invalid thread count. Must be numeric and > 0, is ${threads}`);
       }
       const server = helpers.getServer(ctx, hostname);
-      return runScriptFromScript(Player, "exec", server, scriptname, args, ctx.workerScript, threads);
+      return runScriptFromScript("exec", server, scriptname, args, ctx.workerScript, threads);
     },
   spawn:
     (ctx: NetscriptContext) =>
@@ -740,12 +740,11 @@ const base: InternalAPI<NS> = {
           throw helpers.makeRuntimeErrorMsg(ctx, "Could not find server. This is a bug. Report to dev");
         }
 
-        return runScriptFromScript(Player, "spawn", scriptServer, scriptname, args, ctx.workerScript, threads);
+        return runScriptFromScript("spawn", scriptServer, scriptname, args, ctx.workerScript, threads);
       }, spawnDelay * 1e3);
 
       helpers.log(ctx, () => `Will execute '${scriptname}' in ${spawnDelay} seconds`);
 
-      ctx.workerScript.running = false; // Prevent workerScript from "finishing execution naturally"
       if (killWorkerScript(ctx.workerScript)) {
         helpers.log(ctx, () => "Exiting...");
       }
@@ -820,7 +819,6 @@ const base: InternalAPI<NS> = {
       return scriptsKilled > 0;
     },
   exit: (ctx: NetscriptContext) => (): void => {
-    ctx.workerScript.running = false; // Prevent workerScript from "finishing execution naturally"
     if (killWorkerScript(ctx.workerScript)) {
       helpers.log(ctx, () => "Exiting...");
     } else {
diff --git a/src/NetscriptFunctions/CodingContract.ts b/src/NetscriptFunctions/CodingContract.ts
index 93cd1b7b2..a95dbe0ec 100644
--- a/src/NetscriptFunctions/CodingContract.ts
+++ b/src/NetscriptFunctions/CodingContract.ts
@@ -37,7 +37,7 @@ export function NetscriptCodingContract(): InternalAPI<ICodingContract> {
           throw new Error("The answer provided was not a number, string, or array");
 
         // Convert answer to string.
-        const answerStr = typeof answer === 'string' ? answer : JSON.stringify(answer);
+        const answerStr = typeof answer === "string" ? answer : JSON.stringify(answer);
         const creward = contract.reward;
         if (creward === null) throw new Error("Somehow solved a contract that didn't have a reward");
 
diff --git a/src/NetscriptFunctions/Singularity.ts b/src/NetscriptFunctions/Singularity.ts
index 7ce3893d0..82ef6e3b2 100644
--- a/src/NetscriptFunctions/Singularity.ts
+++ b/src/NetscriptFunctions/Singularity.ts
@@ -92,7 +92,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
         }
         const runningScriptObj = new RunningScript(script, []); // No args
         runningScriptObj.threads = 1; // Only 1 thread
-        startWorkerScript(player, runningScriptObj, home);
+        startWorkerScript(runningScriptObj, home);
       }
     }
   };
@@ -235,8 +235,6 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
           runAfterReset(cbScript);
         }, 0);
 
-        // Prevent ctx.workerScript from "finishing execution naturally"
-        ctx.workerScript.running = false;
         killWorkerScript(ctx.workerScript);
       },
     installAugmentations: (ctx: NetscriptContext) =>
@@ -255,7 +253,6 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
           runAfterReset(cbScript);
         }, 0);
 
-        ctx.workerScript.running = false; // Prevent ctx.workerScript from "finishing execution naturally"
         killWorkerScript(ctx.workerScript);
         return true;
       },
diff --git a/src/NetscriptWorker.ts b/src/NetscriptWorker.ts
index f7ce6e009..6dc79dd0c 100644
--- a/src/NetscriptWorker.ts
+++ b/src/NetscriptWorker.ts
@@ -34,10 +34,8 @@ import { simple as walksimple } from "acorn-walk";
 import { areFilesEqual } from "./Terminal/DirectoryHelpers";
 import { Player } from "./Player";
 import { Terminal } from "./Terminal";
-import { IPlayer } from "./PersonObjects/IPlayer";
 import { ScriptArg } from "./Netscript/ScriptArg";
 import { helpers } from "./Netscript/NetscriptHelpers";
-import { NS } from "./ScriptEditor/NetscriptDefinitions";
 
 // Netscript Ports are instantiated here
 export const NetscriptPorts: IPort[] = [];
@@ -61,10 +59,9 @@ export function prestigeWorkerScripts(): void {
 // JS script promises need a little massaging to have the same guarantees as netscript
 // promises. This does said massaging and kicks the script off. It returns a promise
 // that resolves or rejects when the corresponding worker script is done.
-function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Promise<void> {
-  workerScript.running = true;
+function startNetscript2Script(workerScript: WorkerScript): Promise<void> {
   return new Promise<void>((resolve, reject) => {
-    executeJSScript(player, workerScript.getServer().scripts, workerScript)
+    executeJSScript(Player, workerScript.getServer().scripts, workerScript)
       .then(() => {
         resolve();
       })
@@ -98,7 +95,6 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
 
 function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
   const code = workerScript.code;
-  workerScript.running = true;
 
   //Process imports
   let codeWithImports, codeLineOffset;
@@ -109,120 +105,62 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
   } catch (e: unknown) {
     dialogBoxCreate("Error processing Imports in " + workerScript.name + ":<br>" + String(e));
     workerScript.env.stopFlag = true;
-    workerScript.running = false;
     killWorkerScript(workerScript);
     return Promise.resolve();
   }
 
-  const interpreterInitialization = function (int: Interpreter, scope: unknown): void {
-    interface NS1 extends NS {
+  function wrapNS1Layer(int: Interpreter, intLayer: unknown, path: string[] = []) {
+    //TODO: Better typing layers of interpreter scope and ns
+    interface BasicObject {
       [key: string]: any;
     }
-    //Add the Netscript environment
-    const ns = NetscriptFunctions(workerScript) as NS1;
-    for (const name of Object.keys(ns)) {
-      const entry = ns[name];
+    const nsLayer = path.reduce((prev, newPath) => prev[newPath], workerScript.env.vars as BasicObject);
+    for (const [name, entry] of Object.entries(nsLayer)) {
       if (typeof entry === "function") {
-        //Async functions need to be wrapped. See JS-Interpreter documentation
-        const asyncFuncs = ["hack", "grow", "weaken", "sleep", "prompt", "manualHack", "share", "wget"];
-
-        if (asyncFuncs.includes(name)) {
-          const tempWrapper = function (...args: unknown[]): void {
-            const fnArgs = [];
-
-            //All of the Object/array elements are in JSInterpreter format, so
-            //we have to convert them back to native format to pass them to these fns
-            for (let i = 0; i < args.length - 1; ++i) {
-              if (typeof args[i] === "object" || Array.isArray(args[i])) {
-                fnArgs.push(int.pseudoToNative(args[i]));
-              } else {
-                fnArgs.push(args[i]);
-              }
+        // Async functions need to be wrapped. See JS-Interpreter documentation
+        const wrapper = async (...args: unknown[]) => {
+          // This async wrapper is sent a resolver function as an extra arg.
+          // See JSInterpreter.js:3209
+          try {
+            const callback = args.pop() as (value: unknown) => void;
+            const result = await entry(...args.map(int.pseudoToNative));
+            return callback(int.nativeToPseudo(result));
+          } catch (e: unknown) {
+            // TODO: Unify error handling, this was stolen from previous async handler
+            if (typeof e === "string") {
+              console.error(e);
+              const errorTextArray = e.split("|DELIMITER|");
+              const hostname = errorTextArray[1];
+              const scriptName = errorTextArray[2];
+              const errorMsg = errorTextArray[3];
+              let msg = `${scriptName}@${hostname}<br>`;
+              msg += "<br>";
+              msg += errorMsg;
+              dialogBoxCreate(msg);
+              workerScript.env.stopFlag = true;
+              killWorkerScript(workerScript);
+              return;
             }
-            const callb = args[args.length - 1];
-            const fnPromise = entry(...fnArgs);
-            fnPromise
-              .then(function (res: unknown) {
-                if (typeof callb === "function") {
-                  callb(res);
-                }
-              })
-              .catch(function (err: unknown) {
-                if (typeof err === "string") {
-                  console.error(err);
-                  const errorTextArray = err.split("|DELIMITER|");
-                  const hostname = errorTextArray[1];
-                  const scriptName = errorTextArray[2];
-                  const errorMsg = errorTextArray[3];
-                  let msg = `${scriptName}@${hostname}<br>`;
-                  msg += "<br>";
-                  msg += errorMsg;
-                  dialogBoxCreate(msg);
-                  workerScript.env.stopFlag = true;
-                  workerScript.running = false;
-                  killWorkerScript(workerScript);
-                  return Promise.resolve();
-                }
-              });
-          };
-          int.setProperty(scope, name, int.createAsyncFunction(tempWrapper));
-        } else if (
-          name === "sprintf" ||
-          name === "vsprintf" ||
-          name === "scp" ||
-          name == "write" ||
-          name === "tryWritePort" ||
-          name === "run" ||
-          name === "exec"
-        ) {
-          const tempWrapper = function (...args: unknown[]): void {
-            const fnArgs = [];
-
-            //All of the Object/array elements are in JSInterpreter format, so
-            //we have to convert them back to native format to pass them to these fns
-            for (let i = 0; i < args.length; ++i) {
-              if (typeof args[i] === "object" || Array.isArray(args[i])) {
-                fnArgs.push(int.pseudoToNative(args[i]));
-              } else {
-                fnArgs.push(args[i]);
-              }
-            }
-
-            return entry(...fnArgs);
-          };
-          int.setProperty(scope, name, int.createNativeFunction(tempWrapper));
-        } else {
-          const tempWrapper = function (...args: unknown[]): unknown {
-            const res = entry(...args);
-
-            if (res == null) {
-              return res;
-            } else if (res.constructor === Array || res === Object(res)) {
-              //Objects and Arrays must be converted to the interpreter's format
-              return int.nativeToPseudo(res);
-            } else {
-              return res;
-            }
-          };
-          int.setProperty(scope, name, int.createNativeFunction(tempWrapper));
-        }
+          }
+        };
+        int.setProperty(intLayer, name, int.createAsyncFunction(wrapper));
+      } else if (Array.isArray(entry) || typeof entry !== "object") {
+        // args, strings on enums, etc
+        int.setProperty(intLayer, name, int.nativeToPseudo(entry));
       } else {
-        //bladeburner, or anything else
-        int.setProperty(scope, name, int.nativeToPseudo(entry));
+        // new object layer, e.g. bladeburner
+        int.setProperty(intLayer, name, int.nativeToPseudo({}));
+        wrapNS1Layer(int, (intLayer as BasicObject).properties[name], [...path, name]);
       }
     }
-
-    //Add the arguments
-    int.setProperty(scope, "args", int.nativeToPseudo(workerScript.args));
-  };
+  }
 
   let interpreter: Interpreter;
   try {
-    interpreter = new Interpreter(codeWithImports, interpreterInitialization, codeLineOffset);
+    interpreter = new Interpreter(codeWithImports, wrapNS1Layer, codeLineOffset);
   } catch (e: unknown) {
     dialogBoxCreate("Syntax ERROR in " + workerScript.name + ":<br>" + String(e));
     workerScript.env.stopFlag = true;
-    workerScript.running = false;
     killWorkerScript(workerScript);
     return Promise.resolve();
   }
@@ -421,13 +359,8 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): { c
  * corresponding WorkerScript), and add the RunningScript to the server on which
  * it is active
  */
-export function startWorkerScript(
-  player: IPlayer,
-  runningScript: RunningScript,
-  server: BaseServer,
-  parent?: WorkerScript,
-): number {
-  if (createAndAddWorkerScript(player, runningScript, server, parent)) {
+export function startWorkerScript(runningScript: RunningScript, server: BaseServer, parent?: WorkerScript): number {
+  if (createAndAddWorkerScript(runningScript, server, parent)) {
     // Push onto runningScripts.
     // This has to come after createAndAddWorkerScript() because that fn updates RAM usage
     server.runScript(runningScript);
@@ -447,12 +380,7 @@ export function startWorkerScript(
  * @param {Server} server - Server on which the script is to be run
  * returns {boolean} indicating whether or not the workerScript was successfully added
  */
-function createAndAddWorkerScript(
-  player: IPlayer,
-  runningScriptObj: RunningScript,
-  server: BaseServer,
-  parent?: WorkerScript,
-): boolean {
+function createAndAddWorkerScript(runningScriptObj: RunningScript, server: BaseServer, parent?: WorkerScript): boolean {
   // Update server's ram usage
   let threads = 1;
   if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) {
@@ -496,7 +424,7 @@ function createAndAddWorkerScript(
   // Start the script's execution
   let scriptExecution: Promise<void> | null = null; // Script's resulting promise
   if (workerScript.name.endsWith(".js")) {
-    scriptExecution = startNetscript2Script(player, workerScript);
+    scriptExecution = startNetscript2Script(workerScript);
   } else {
     scriptExecution = startNetscript1Script(workerScript);
     if (!(scriptExecution instanceof Promise)) {
@@ -508,14 +436,11 @@ function createAndAddWorkerScript(
   // running status to false
   scriptExecution
     .then(function () {
-      workerScript.running = false;
       workerScript.env.stopFlag = true;
       // On natural death, the earnings are transfered to the parent if it still exists.
-      if (parent !== undefined) {
-        if (parent.running) {
-          parent.scriptRef.onlineExpGained += runningScriptObj.onlineExpGained;
-          parent.scriptRef.onlineMoneyMade += runningScriptObj.onlineMoneyMade;
-        }
+      if (parent !== undefined && !parent.env.stopFlag) {
+        parent.scriptRef.onlineExpGained += runningScriptObj.onlineExpGained;
+        parent.scriptRef.onlineMoneyMade += runningScriptObj.onlineMoneyMade;
       }
 
       killWorkerScript(workerScript);
@@ -583,7 +508,7 @@ export function updateOnlineScriptTimes(numCycles = 1): void {
  * Called when the game is loaded. Loads all running scripts (from all servers)
  * into worker scripts so that they will start running
  */
-export function loadAllRunningScripts(player: IPlayer): void {
+export function loadAllRunningScripts(): void {
   const skipScriptLoad = window.location.href.toLowerCase().indexOf("?noscripts") !== -1;
   if (skipScriptLoad) {
     Terminal.warn("Skipped loading player scripts during startup");
@@ -604,7 +529,7 @@ export function loadAllRunningScripts(player: IPlayer): void {
     } else {
       for (let j = 0; j < server.runningScripts.length; ++j) {
         const fileName = server.runningScripts[j].filename;
-        createAndAddWorkerScript(player, server.runningScripts[j], server);
+        createAndAddWorkerScript(server.runningScripts[j], server);
 
         if (!server.runningScripts[j]) {
           // createAndAddWorkerScript can modify the server.runningScripts array if a script is invalid
@@ -623,7 +548,6 @@ export function loadAllRunningScripts(player: IPlayer): void {
  * Run a script from inside another script (run(), exec(), spawn(), etc.)
  */
 export function runScriptFromScript(
-  player: IPlayer,
   caller: string,
   server: BaseServer,
   scriptname: string,
@@ -698,7 +622,7 @@ export function runScriptFromScript(
     runningScriptObj.threads = threads;
     runningScriptObj.server = server.hostname;
 
-    return startWorkerScript(player, runningScriptObj, server, workerScript);
+    return startWorkerScript(runningScriptObj, server, workerScript);
   }
 
   workerScript.log(caller, () => `Could not find script '${scriptname}' on '${server.hostname}'`);
diff --git a/src/Terminal/commands/runScript.ts b/src/Terminal/commands/runScript.ts
index 93e0d2ccb..baa18e589 100644
--- a/src/Terminal/commands/runScript.ts
+++ b/src/Terminal/commands/runScript.ts
@@ -76,7 +76,7 @@ export function runScript(
     const runningScript = new RunningScript(script, args);
     runningScript.threads = numThreads;
 
-    const success = startWorkerScript(player, runningScript, server);
+    const success = startWorkerScript(runningScript, server);
     if (!success) {
       terminal.error(`Failed to start script`);
       return;
diff --git a/src/engine.tsx b/src/engine.tsx
index a4812d943..db143f9aa 100644
--- a/src/engine.tsx
+++ b/src/engine.tsx
@@ -278,7 +278,7 @@ const Engine: {
       Player.gainMoney(offlineHackingIncome, "hacking");
       // Process offline progress
 
-      loadAllRunningScripts(Player); // This also takes care of offline production for those scripts
+      loadAllRunningScripts(); // This also takes care of offline production for those scripts
 
       if (Player.currentWork !== null) {
         Player.focus = true;
diff --git a/src/ui/React/LogBoxManager.tsx b/src/ui/React/LogBoxManager.tsx
index a4c309d76..a9a2e6479 100644
--- a/src/ui/React/LogBoxManager.tsx
+++ b/src/ui/React/LogBoxManager.tsx
@@ -15,7 +15,6 @@ import { workerScripts } from "../../Netscript/WorkerScripts";
 import { startWorkerScript } from "../../NetscriptWorker";
 import { GetServer } from "../../Server/AllServers";
 import { findRunningScript } from "../../Script/ScriptHelpers";
-import { Player } from "../../Player";
 import { debounce } from "lodash";
 import { Settings } from "../../Settings/Settings";
 import { ANSIITypography } from "./ANSIITypography";
@@ -159,7 +158,7 @@ function LogWindow(props: IProps): React.ReactElement {
     if (server === null) return;
     const s = findRunningScript(script.filename, script.args, server);
     if (s === null) {
-      startWorkerScript(Player, script, server);
+      startWorkerScript(script, server);
     } else {
       setScript(s);
     }