newmark-agent 0.5.10 → 0.5.11

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.
@@ -8383,7 +8383,7 @@ var require_createWorker = __commonJS({
8383
8383
  ...defaultOptions,
8384
8384
  ..._options
8385
8385
  });
8386
- const promises2 = {};
8386
+ const promises3 = {};
8387
8387
  const currentLangs = typeof langs === "string" ? langs.split("+") : langs;
8388
8388
  let currentOem = oem;
8389
8389
  let currentConfig = config;
@@ -8403,7 +8403,7 @@ var require_createWorker = __commonJS({
8403
8403
  const startJob = ({ id: jobId, action, payload }) => new Promise((resolve16, reject) => {
8404
8404
  log2(`[${id}]: Start ${jobId}, action=${action}`);
8405
8405
  const promiseId = `${action}-${jobId}`;
8406
- promises2[promiseId] = { resolve: resolve16, reject };
8406
+ promises3[promiseId] = { resolve: resolve16, reject };
8407
8407
  send(worker, {
8408
8408
  workerId: id,
8409
8409
  jobId,
@@ -8508,11 +8508,11 @@ var require_createWorker = __commonJS({
8508
8508
  const promiseId = `${action}-${jobId}`;
8509
8509
  if (status === "resolve") {
8510
8510
  log2(`[${workerId}]: Complete ${jobId}`);
8511
- promises2[promiseId].resolve({ jobId, data });
8512
- delete promises2[promiseId];
8511
+ promises3[promiseId].resolve({ jobId, data });
8512
+ delete promises3[promiseId];
8513
8513
  } else if (status === "reject") {
8514
- promises2[promiseId].reject(data);
8515
- delete promises2[promiseId];
8514
+ promises3[promiseId].reject(data);
8515
+ delete promises3[promiseId];
8516
8516
  if (action === "load") workerResReject(data);
8517
8517
  if (errorHandler) {
8518
8518
  errorHandler(data);
@@ -54006,18 +54006,18 @@ var require_proxy_agent = __commonJS({
54006
54006
  }
54007
54007
  }
54008
54008
  [kClose]() {
54009
- const promises2 = [this[kAgent].close()];
54009
+ const promises3 = [this[kAgent].close()];
54010
54010
  if (this[kClient]) {
54011
- promises2.push(this[kClient].close());
54011
+ promises3.push(this[kClient].close());
54012
54012
  }
54013
- return Promise.all(promises2);
54013
+ return Promise.all(promises3);
54014
54014
  }
54015
54015
  [kDestroy]() {
54016
- const promises2 = [this[kAgent].destroy()];
54016
+ const promises3 = [this[kAgent].destroy()];
54017
54017
  if (this[kClient]) {
54018
- promises2.push(this[kClient].destroy());
54018
+ promises3.push(this[kClient].destroy());
54019
54019
  }
54020
- return Promise.all(promises2);
54020
+ return Promise.all(promises3);
54021
54021
  }
54022
54022
  };
54023
54023
  function buildHeaders(headers) {
@@ -344923,6 +344923,7 @@ var fs24 = __toESM(require("fs"));
344923
344923
  var path27 = __toESM(require("path"));
344924
344924
  var import_crypto14 = require("crypto");
344925
344925
  var processStates = /* @__PURE__ */ new Map();
344926
+ var preparedPreviousStates = /* @__PURE__ */ new Map();
344926
344927
  function stateKey(root2, role) {
344927
344928
  return `${path27.resolve(root2)}\0${role}`;
344928
344929
  }
@@ -344968,7 +344969,8 @@ function beginRuntimeLifecycle(root2, role = "main") {
344968
344969
  const key3 = stateKey(root2, role);
344969
344970
  const existingProcessState = processStates.get(key3);
344970
344971
  if (existingProcessState) return existingProcessState;
344971
- const previousStates = readActiveStates(root2, role);
344972
+ const previousStates = preparedPreviousStates.get(key3) || readActiveStates(root2, role);
344973
+ preparedPreviousStates.delete(key3);
344972
344974
  const previousOwnerAlive = previousStates.some((previous) => isRuntimeProcessAlive(Number(previous.pid)));
344973
344975
  const state = {
344974
344976
  role,
@@ -344991,12 +344993,12 @@ function markRuntimeLifecycleClean(root2, role = "main") {
344991
344993
  const current = processStates.get(key3);
344992
344994
  if (!current) return;
344993
344995
  try {
344994
- writeState(root2, role, {
344995
- ...current,
344996
- active: false,
344997
- cleanExitAt: (/* @__PURE__ */ new Date()).toISOString()
344998
- });
344996
+ fs24.unlinkSync(statePath(root2, role, current.ownerId));
344999
344997
  } catch {
344998
+ try {
344999
+ writeState(root2, role, { ...current, active: false, cleanExitAt: (/* @__PURE__ */ new Date()).toISOString() });
345000
+ } catch {
345001
+ }
345000
345002
  }
345001
345003
  }
345002
345004
 
@@ -9,6 +9,8 @@ export interface RuntimeLifecycleState {
9
9
  active: true;
10
10
  }
11
11
  export declare function isRuntimeProcessAlive(pid: number): boolean;
12
+ /** Prepare crash-recovery markers asynchronously after the startup shell is visible. */
13
+ export declare function prepareRuntimeLifecycle(root: string, role?: RuntimeLifecycleRole): Promise<void>;
12
14
  /**
13
15
  * Claim this process/role as the current runtime owner.
14
16
  *
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.isRuntimeProcessAlive = isRuntimeProcessAlive;
37
+ exports.prepareRuntimeLifecycle = prepareRuntimeLifecycle;
37
38
  exports.beginRuntimeLifecycle = beginRuntimeLifecycle;
38
39
  exports.markRuntimeLifecycleClean = markRuntimeLifecycleClean;
39
40
  exports.runtimeLifecycleState = runtimeLifecycleState;
@@ -41,6 +42,8 @@ const fs = __importStar(require("fs"));
41
42
  const path = __importStar(require("path"));
42
43
  const crypto_1 = require("crypto");
43
44
  const processStates = new Map();
45
+ const preparedPreviousStates = new Map();
46
+ const lifecyclePreparations = new Map();
44
47
  function stateKey(root, role) {
45
48
  return `${path.resolve(root)}\u0000${role}`;
46
49
  }
@@ -89,6 +92,50 @@ function readActiveStates(root, role) {
89
92
  return [];
90
93
  }
91
94
  }
95
+ /** Prepare crash-recovery markers asynchronously after the startup shell is visible. */
96
+ function prepareRuntimeLifecycle(root, role = 'main') {
97
+ const key = stateKey(root, role);
98
+ if (processStates.has(key) || preparedPreviousStates.has(key))
99
+ return Promise.resolve();
100
+ const current = lifecyclePreparations.get(key);
101
+ if (current)
102
+ return current.then(() => undefined);
103
+ const preparation = (async () => {
104
+ const directory = path.join(root, '.newmark-runtime');
105
+ let files;
106
+ try {
107
+ files = (await fs.promises.readdir(directory))
108
+ .filter(file => file.startsWith(`lifecycle-${role}`) && file.endsWith('.json'));
109
+ }
110
+ catch {
111
+ return [];
112
+ }
113
+ const active = [];
114
+ for (let offset = 0; offset < files.length; offset += 24) {
115
+ const states = await Promise.all(files.slice(offset, offset + 24).map(async (file) => {
116
+ const filePath = path.join(directory, file);
117
+ try {
118
+ const state = JSON.parse(await fs.promises.readFile(filePath, 'utf-8'));
119
+ if (state?.active === true)
120
+ return state;
121
+ }
122
+ catch {
123
+ // A corrupt marker cannot establish a live owner.
124
+ }
125
+ await fs.promises.unlink(filePath).catch(() => undefined);
126
+ return null;
127
+ }));
128
+ for (const state of states)
129
+ if (state)
130
+ active.push(state);
131
+ }
132
+ return active;
133
+ })();
134
+ lifecyclePreparations.set(key, preparation);
135
+ return preparation.then(states => {
136
+ preparedPreviousStates.set(key, states);
137
+ }).finally(() => lifecyclePreparations.delete(key));
138
+ }
92
139
  /**
93
140
  * Claim this process/role as the current runtime owner.
94
141
  *
@@ -101,7 +148,8 @@ function beginRuntimeLifecycle(root, role = 'main') {
101
148
  const existingProcessState = processStates.get(key);
102
149
  if (existingProcessState)
103
150
  return existingProcessState;
104
- const previousStates = readActiveStates(root, role);
151
+ const previousStates = preparedPreviousStates.get(key) || readActiveStates(root, role);
152
+ preparedPreviousStates.delete(key);
105
153
  const previousOwnerAlive = previousStates.some(previous => isRuntimeProcessAlive(Number(previous.pid)));
106
154
  const state = {
107
155
  role,
@@ -129,15 +177,16 @@ function markRuntimeLifecycleClean(root, role = 'main') {
129
177
  if (!current)
130
178
  return;
131
179
  try {
132
- writeState(root, role, {
133
- ...current,
134
- active: false,
135
- cleanExitAt: new Date().toISOString(),
136
- });
180
+ fs.unlinkSync(statePath(root, role, current.ownerId));
137
181
  }
138
182
  catch {
139
- // Leaving the marker active is safer than claiming a clean exit after a
140
- // failed durable write.
183
+ try {
184
+ writeState(root, role, { ...current, active: false, cleanExitAt: new Date().toISOString() });
185
+ }
186
+ catch {
187
+ // Leaving the marker active is safer than claiming a clean exit after a
188
+ // failed durable write.
189
+ }
141
190
  }
142
191
  }
143
192
  function runtimeLifecycleState(root, role = 'main') {
package/dist/main.js CHANGED
@@ -1803,12 +1803,10 @@ else {
1803
1803
  };
1804
1804
  let firstRunInitialized = false;
1805
1805
  if (!automationWakeMode) {
1806
- // Attempt one loads the real desktop immediately. The renderer keeps a
1807
- // startup cover above it until the main-process readiness acknowledgement,
1808
- // avoiding a startup.html -> index.html renderer navigation on the hot path.
1809
- startupAttempt = 1;
1810
- startupShellLoadedAt = Date.now();
1811
- startupWindow = createDesktopWindow(true, true, startupAttempt);
1806
+ // Keep Chromium's heavy index renderer off the event loop until the
1807
+ // lightweight startup shell is painted and durable recovery is ready.
1808
+ startupAttempt = 0;
1809
+ startupWindow = createDesktopWindow(false, true);
1812
1810
  mainWindow = startupWindow;
1813
1811
  createTray();
1814
1812
  }
@@ -1901,6 +1899,8 @@ else {
1901
1899
  recordStartup('first-run-init');
1902
1900
  }
1903
1901
  if (!agent) {
1902
+ await (0, runtimeLifecycle_1.prepareRuntimeLifecycle)(root, 'main');
1903
+ recordStartup('runtime-lifecycle-prepared');
1904
1904
  agent = new agent_1.Agent(root);
1905
1905
  mcpManager = new mcpManager_1.McpManager(root);
1906
1906
  activeAgentBackendMode = process.platform === 'win32' && agent.config.getBool('agent', 'run_in_wsl') ? 'wsl' : 'windows';
@@ -8383,7 +8383,7 @@ var require_createWorker = __commonJS({
8383
8383
  ...defaultOptions,
8384
8384
  ..._options
8385
8385
  });
8386
- const promises2 = {};
8386
+ const promises3 = {};
8387
8387
  const currentLangs = typeof langs === "string" ? langs.split("+") : langs;
8388
8388
  let currentOem = oem;
8389
8389
  let currentConfig = config;
@@ -8403,7 +8403,7 @@ var require_createWorker = __commonJS({
8403
8403
  const startJob = ({ id: jobId, action, payload }) => new Promise((resolve16, reject) => {
8404
8404
  log2(`[${id}]: Start ${jobId}, action=${action}`);
8405
8405
  const promiseId = `${action}-${jobId}`;
8406
- promises2[promiseId] = { resolve: resolve16, reject };
8406
+ promises3[promiseId] = { resolve: resolve16, reject };
8407
8407
  send(worker, {
8408
8408
  workerId: id,
8409
8409
  jobId,
@@ -8508,11 +8508,11 @@ var require_createWorker = __commonJS({
8508
8508
  const promiseId = `${action}-${jobId}`;
8509
8509
  if (status === "resolve") {
8510
8510
  log2(`[${workerId}]: Complete ${jobId}`);
8511
- promises2[promiseId].resolve({ jobId, data });
8512
- delete promises2[promiseId];
8511
+ promises3[promiseId].resolve({ jobId, data });
8512
+ delete promises3[promiseId];
8513
8513
  } else if (status === "reject") {
8514
- promises2[promiseId].reject(data);
8515
- delete promises2[promiseId];
8514
+ promises3[promiseId].reject(data);
8515
+ delete promises3[promiseId];
8516
8516
  if (action === "load") workerResReject(data);
8517
8517
  if (errorHandler) {
8518
8518
  errorHandler(data);
@@ -54006,18 +54006,18 @@ var require_proxy_agent = __commonJS({
54006
54006
  }
54007
54007
  }
54008
54008
  [kClose]() {
54009
- const promises2 = [this[kAgent].close()];
54009
+ const promises3 = [this[kAgent].close()];
54010
54010
  if (this[kClient]) {
54011
- promises2.push(this[kClient].close());
54011
+ promises3.push(this[kClient].close());
54012
54012
  }
54013
- return Promise.all(promises2);
54013
+ return Promise.all(promises3);
54014
54014
  }
54015
54015
  [kDestroy]() {
54016
- const promises2 = [this[kAgent].destroy()];
54016
+ const promises3 = [this[kAgent].destroy()];
54017
54017
  if (this[kClient]) {
54018
- promises2.push(this[kClient].destroy());
54018
+ promises3.push(this[kClient].destroy());
54019
54019
  }
54020
- return Promise.all(promises2);
54020
+ return Promise.all(promises3);
54021
54021
  }
54022
54022
  };
54023
54023
  function buildHeaders(headers) {
@@ -344927,6 +344927,7 @@ var fs24 = __toESM(require("fs"));
344927
344927
  var path27 = __toESM(require("path"));
344928
344928
  var import_crypto14 = require("crypto");
344929
344929
  var processStates = /* @__PURE__ */ new Map();
344930
+ var preparedPreviousStates = /* @__PURE__ */ new Map();
344930
344931
  function stateKey(root2, role) {
344931
344932
  return `${path27.resolve(root2)}\0${role}`;
344932
344933
  }
@@ -344972,7 +344973,8 @@ function beginRuntimeLifecycle(root2, role = "main") {
344972
344973
  const key3 = stateKey(root2, role);
344973
344974
  const existingProcessState = processStates.get(key3);
344974
344975
  if (existingProcessState) return existingProcessState;
344975
- const previousStates = readActiveStates(root2, role);
344976
+ const previousStates = preparedPreviousStates.get(key3) || readActiveStates(root2, role);
344977
+ preparedPreviousStates.delete(key3);
344976
344978
  const previousOwnerAlive = previousStates.some((previous) => isRuntimeProcessAlive(Number(previous.pid)));
344977
344979
  const state = {
344978
344980
  role,
@@ -344995,12 +344997,12 @@ function markRuntimeLifecycleClean(root2, role = "main") {
344995
344997
  const current = processStates.get(key3);
344996
344998
  if (!current) return;
344997
344999
  try {
344998
- writeState(root2, role, {
344999
- ...current,
345000
- active: false,
345001
- cleanExitAt: (/* @__PURE__ */ new Date()).toISOString()
345002
- });
345000
+ fs24.unlinkSync(statePath(root2, role, current.ownerId));
345003
345001
  } catch {
345002
+ try {
345003
+ writeState(root2, role, { ...current, active: false, cleanExitAt: (/* @__PURE__ */ new Date()).toISOString() });
345004
+ } catch {
345005
+ }
345004
345006
  }
345005
345007
  }
345006
345008
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "newmark-agent",
3
3
  "productName": "Newmark Agent",
4
- "version": "0.5.10",
4
+ "version": "0.5.11",
5
5
  "description": "Newmark Agent — Portable AI coding agent with rich GUI and CLI (TypeScript)",
6
6
  "homepage": "https://github.com/positer/Newmark-Agent",
7
7
  "repository": {
@@ -82,6 +82,7 @@
82
82
  "test:tool-process": "npm run build && node dist/tests/toolProcessVerify.js",
83
83
  "test:real-tool-concurrency": "npm run build && node dist/tests/realProviderToolConcurrencyStress.js",
84
84
  "test:startup-prewarm": "npm run build && node dist/tests/startupPrewarmVerify.js",
85
+ "test:startup-responsiveness": "npm run build && node dist/tests/runtimeLifecycleStartupVerify.js && node dist/tests/startupPrewarmVerify.js && node scripts/measure-startup-responsiveness.cjs",
85
86
  "test:browser-use-electron": "npm run build && electron dist/tests/browserUseElectronVerify.js",
86
87
  "test:dev009": "npm run build && node dist/tests/dev009Verify.js && node dist/tests/runtimeIsolationVerify.js && node dist/tests/guideWorkRunVerify.js && node dist/tests/guideUiReconcileVerify.js && node dist/tests/queueAttachmentIsolationVerify.js && node dist/tests/workspaceMenuVerify.js && node dist/tests/userImagePersistenceVerify.js && node dist/tests/visualPreferencesVerify.js && node dist/tests/browserUseVerify.js && node dist/tests/toolProcessVerify.js && node dist/tests/startupPrewarmVerify.js && node dist/tests/pdfPreviewServerVerify.js && node dist/tests/editorLifecycleVerify.js && node dist/tests/computerUsePerformanceVerify.js && electron dist/tests/browserUseElectronVerify.js",
87
88
  "test:dev008": "npm run build && node dist/tests/dev008-subagent.js",