newmark-agent 0.3.10 → 0.3.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.
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultComputerUseSessionRegistry = exports.ComputerUseSessionRegistry = exports.COMPUTER_USE_LOCK_TTL_MS = exports.COMPUTER_USE_OCCUPIED_MARKER = void 0;
4
+ exports.COMPUTER_USE_OCCUPIED_MARKER = 'computerUse occupied';
5
+ exports.COMPUTER_USE_LOCK_TTL_MS = 10 * 60 * 1000;
6
+ class ComputerUseSessionRegistry {
7
+ ttlMs;
8
+ enabledByRuntime = new Map();
9
+ activeLease = null;
10
+ constructor(ttlMs = exports.COMPUTER_USE_LOCK_TTL_MS) {
11
+ this.ttlMs = ttlMs;
12
+ }
13
+ authorize(action, scope, dryRun = false) {
14
+ const normalizedAction = String(action || '').trim().toLowerCase();
15
+ const runtimeKey = String(scope.runtimeKey || '').trim() || 'conversation:default';
16
+ const now = Date.now();
17
+ this.clearExpired(now);
18
+ if (this.activeLease && this.activeLease.runtimeKey !== runtimeKey) {
19
+ return this.occupiedError(normalizedAction, scope.ownerLabel, this.activeLease.ownerLabel);
20
+ }
21
+ if (normalizedAction === 'takeover_stop')
22
+ return null;
23
+ const enabled = this.enabledByRuntime.get(runtimeKey) !== false;
24
+ const readOnly = normalizedAction === 'observe' || normalizedAction === 'app_list' || normalizedAction === 'app_observe' || normalizedAction === 'wait';
25
+ if (!enabled && !readOnly && !dryRun && normalizedAction !== 'takeover_start') {
26
+ return JSON.stringify({
27
+ ok: false,
28
+ action: normalizedAction,
29
+ error: `ComputerUse is disabled for ${scope.ownerLabel}. Enable ComputerUse for this conversation before sending desktop operations.`,
30
+ computer_use_enabled: false,
31
+ requested_owner: scope.ownerLabel,
32
+ }, null, 2);
33
+ }
34
+ if (normalizedAction === 'takeover_start')
35
+ this.enabledByRuntime.set(runtimeKey, true);
36
+ if (!this.activeLease) {
37
+ this.activeLease = { ...scope, runtimeKey, updatedAt: now };
38
+ }
39
+ else {
40
+ this.activeLease.updatedAt = now;
41
+ }
42
+ return null;
43
+ }
44
+ complete(action, scope) {
45
+ const normalizedAction = String(action || '').trim().toLowerCase();
46
+ const runtimeKey = String(scope.runtimeKey || '').trim() || 'conversation:default';
47
+ if (normalizedAction === 'takeover_stop') {
48
+ if (!this.activeLease || this.activeLease.runtimeKey === runtimeKey)
49
+ this.activeLease = null;
50
+ this.enabledByRuntime.set(runtimeKey, false);
51
+ return;
52
+ }
53
+ if (this.activeLease?.runtimeKey === runtimeKey)
54
+ this.activeLease.updatedAt = Date.now();
55
+ }
56
+ setEnabled(scope, enabled) {
57
+ const runtimeKey = String(scope.runtimeKey || '').trim() || 'conversation:default';
58
+ this.clearExpired();
59
+ if (this.activeLease && this.activeLease.runtimeKey !== runtimeKey) {
60
+ return { ok: false, error: this.occupiedError('toggle', scope.ownerLabel, this.activeLease.ownerLabel), state: this.state(runtimeKey) };
61
+ }
62
+ this.enabledByRuntime.set(runtimeKey, enabled !== false);
63
+ if (enabled === false && this.activeLease?.runtimeKey === runtimeKey)
64
+ this.activeLease = null;
65
+ return { ok: true, state: this.state(runtimeKey) };
66
+ }
67
+ state(runtimeKey) {
68
+ const key = String(runtimeKey || '').trim() || 'conversation:default';
69
+ this.clearExpired();
70
+ const lease = this.activeLease;
71
+ return {
72
+ runtimeKey: key,
73
+ enabled: this.enabledByRuntime.get(key) !== false,
74
+ occupied: !!lease,
75
+ ...(lease ? { ownerLabel: lease.ownerLabel, updatedAt: lease.updatedAt } : {}),
76
+ };
77
+ }
78
+ cancelTarget(runtimeKey) {
79
+ const key = String(runtimeKey || '').trim();
80
+ if (!key)
81
+ return false;
82
+ const hadActiveLease = this.activeLease?.runtimeKey === key;
83
+ if (hadActiveLease)
84
+ this.activeLease = null;
85
+ this.enabledByRuntime.set(key, false);
86
+ return hadActiveLease;
87
+ }
88
+ clearExpired(now = Date.now()) {
89
+ if (this.activeLease && now - this.activeLease.updatedAt > this.ttlMs) {
90
+ this.activeLease = null;
91
+ }
92
+ }
93
+ occupiedError(action, requestedOwner, activeOwner) {
94
+ return JSON.stringify({
95
+ ok: false,
96
+ action,
97
+ error: `${exports.COMPUTER_USE_OCCUPIED_MARKER}: ComputerUse is already active in ${activeOwner}. Stop it with computer_use takeover_stop or wait before another conversation takes control.`,
98
+ lock_owner: activeOwner,
99
+ requested_owner: requestedOwner,
100
+ }, null, 2);
101
+ }
102
+ }
103
+ exports.ComputerUseSessionRegistry = ComputerUseSessionRegistry;
104
+ exports.defaultComputerUseSessionRegistry = new ComputerUseSessionRegistry();
105
+ //# sourceMappingURL=computerUseSession.js.map
@@ -212,6 +212,7 @@ export declare class ConversationKernel {
212
212
  private processTimeoutMs;
213
213
  private runtime;
214
214
  private scheduleGoalContinuation;
215
+ private schedulePendingRuntimeContinuation;
215
216
  private startGoalDrivenBuild;
216
217
  private createRunner;
217
218
  private enqueueRootInboxWake;
@@ -217,6 +217,7 @@ class ConversationKernel {
217
217
  runtime.runner.recordGuideReceipt(deferred);
218
218
  this.emitQueueUpdate(runtime);
219
219
  this.activateAcceptedGoal(runtime, envelope.goalObjective);
220
+ this.schedulePendingRuntimeContinuation(runtime, runtime.runId);
220
221
  return deferred;
221
222
  }
222
223
  if (runtime.guideAcceptanceClosedRunId === runtime.runId) {
@@ -248,6 +249,7 @@ class ConversationKernel {
248
249
  createdAt: deferred.createdAt,
249
250
  }]);
250
251
  this.activateAcceptedGoal(runtime, envelope.goalObjective);
252
+ this.schedulePendingRuntimeContinuation(runtime, runtime.runId);
251
253
  return deferred;
252
254
  }
253
255
  const queued = runtime.runner.queueActiveKernelMessage(safeEnvelope.text, 'steer', clientMessageId, runtime.runId, safeEnvelope.images);
@@ -278,6 +280,7 @@ class ConversationKernel {
278
280
  createdAt: deferred.createdAt,
279
281
  }]);
280
282
  this.activateAcceptedGoal(runtime, envelope.goalObjective);
283
+ this.schedulePendingRuntimeContinuation(runtime, runtime.runId);
281
284
  return deferred;
282
285
  }
283
286
  checkpoint(target) {
@@ -507,6 +510,12 @@ class ConversationKernel {
507
510
  stopped = true;
508
511
  this.settleCooperativeStop(runtime, runId);
509
512
  }
513
+ else if (runtime.pendingNextTurn.length > 0) {
514
+ // A renderer/IPC Guide can arrive after the final-drain barrier's
515
+ // last check but before this promise settles. Do not leave the
516
+ // deferred continuation queued on an idle runtime.
517
+ this.schedulePendingRuntimeContinuation(runtime, runId);
518
+ }
510
519
  }
511
520
  }
512
521
  if (!stopped && runtime.runId === runId)
@@ -656,6 +665,7 @@ class ConversationKernel {
656
665
  guideReceipts: new Map(),
657
666
  guideEnvelopes: new Map(),
658
667
  goalContinuationTimer: undefined,
668
+ pendingContinuationRunId: undefined,
659
669
  };
660
670
  runner.setGoalContinuationGate(() => {
661
671
  this.queueState(runtime);
@@ -748,6 +758,34 @@ class ConversationKernel {
748
758
  });
749
759
  }, 250);
750
760
  }
761
+ schedulePendingRuntimeContinuation(runtime, runId) {
762
+ if (runtime.pendingContinuationRunId === runId)
763
+ return;
764
+ runtime.pendingContinuationRunId = runId;
765
+ const active = runtime.activePromise;
766
+ if (active) {
767
+ const continueAfterSettlement = () => {
768
+ runtime.pendingContinuationRunId = undefined;
769
+ this.schedulePendingRuntimeContinuation(runtime, runId);
770
+ };
771
+ void active.then(continueAfterSettlement, continueAfterSettlement);
772
+ return;
773
+ }
774
+ setImmediate(() => {
775
+ runtime.pendingContinuationRunId = undefined;
776
+ if (runtime.runId !== runId || runtime.activePromise || runtime.stopRequestedRunId === runId)
777
+ return;
778
+ const next = runtime.pendingNextTurn.shift();
779
+ if (!next)
780
+ return;
781
+ const message = typeof next.message === 'string'
782
+ ? { text: next.message, runId }
783
+ : { ...next.message, runId: next.message.runId || runId };
784
+ void this.prompt(message, runtime.target, runtime.options, next.queueMode).catch(() => {
785
+ // Agent.process and the work-run finalizer already publish the error.
786
+ });
787
+ });
788
+ }
751
789
  startGoalDrivenBuild(runtime) {
752
790
  if (runtime.goalContinuationTimer) {
753
791
  clearTimeout(runtime.goalContinuationTimer);
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ElectronBrowserUseHost = void 0;
4
+ const browserUsePageAdapter_1 = require("./browserUsePageAdapter");
4
5
  const SAFE_NAVIGATION = /^(?:https?:|about:blank|newmark-preview:)/i;
5
6
  const BROWSER_USE_WORLD_ID = 999;
6
7
  /**
@@ -109,6 +110,12 @@ class ElectronBrowserUseHost {
109
110
  contents.sendInputEvent({ type: 'mouseUp', button: 'left', clickCount: 1, ...point });
110
111
  await abortableDelay(10, signal);
111
112
  },
113
+ clickElement: contents.getType() === 'webview' ? async (token, signal) => {
114
+ throwIfAborted(signal);
115
+ const result = await raceWithAbort(contents.executeJavaScriptInIsolatedWorld(BROWSER_USE_WORLD_ID, [{ code: (0, browserUsePageAdapter_1.browserUseClickScript)(token) }], true), signal);
116
+ if (!result?.clicked)
117
+ throw new Error(result?.error || 'Unable to click the observed Browser-Use element.');
118
+ } : undefined,
112
119
  replaceFocusedText: async (text, signal) => {
113
120
  throwIfAborted(signal);
114
121
  contents.focus();
@@ -473,6 +473,70 @@ async function snapshotWindowsProcessTree(rootPid, timeoutMs = WINDOWS_TREE_SNAP
473
473
  function delay(ms) {
474
474
  return new Promise(resolve => setTimeout(resolve, Math.max(0, ms)));
475
475
  }
476
+ function processRootIsAlive(pid) {
477
+ if (!Number.isInteger(pid) || pid <= 0)
478
+ return false;
479
+ try {
480
+ process.kill(pid, 0);
481
+ return true;
482
+ }
483
+ catch {
484
+ return false;
485
+ }
486
+ }
487
+ /**
488
+ * A graceful utility shutdown can make Electron's UtilityProcess.kill() return
489
+ * false before the child exit event is delivered. On Windows the captured
490
+ * root creation identity lets us prove that the root and every owned
491
+ * descendant are gone without falling back to an unscoped PID kill.
492
+ */
493
+ async function confirmWindowsUtilityProcessTreeStopped(pid, expectedRootCreationIdentity, options, ownerKey) {
494
+ if (process.platform !== 'win32' || !Number.isInteger(pid) || pid <= 0 || !/^\d+$/.test(expectedRootCreationIdentity))
495
+ return false;
496
+ const snapshotter = options.snapshot || snapshotWindowsProcessTree;
497
+ const deadline = Date.now() + Math.min(WINDOWS_TREE_FORCE_STOP_DEADLINE_MS, Math.max(1, Math.floor(options.forceStopDeadlineMs ?? WINDOWS_TREE_FORCE_STOP_DEADLINE_MS)));
498
+ const maxRescans = Math.max(2, Math.floor(options.maxRescans ?? WINDOWS_TREE_MAX_RESCANS));
499
+ const stableRequired = Math.max(2, Math.min(maxRescans, Math.floor(options.stableEmptyRescans ?? WINDOWS_TREE_STABLE_EMPTY_RESCANS)));
500
+ const syntheticRoot = {
501
+ pid,
502
+ parentPid: 0,
503
+ depth: 0,
504
+ creationIdentity: expectedRootCreationIdentity,
505
+ };
506
+ let stableEmpty = 0;
507
+ for (let scan = 0; scan < maxRescans; scan++) {
508
+ try {
509
+ if (scan > 0)
510
+ await withinWindowsTreeDeadline(delay(options.rescanDelayMs ?? WINDOWS_TREE_RESCAN_DELAY_MS), deadline, 'graceful-stop rescan delay');
511
+ const snapshot = await withinWindowsTreeDeadline(snapshotter(pid, remainingWindowsTreeBudget(deadline, options.rescanTimeoutMs ?? WINDOWS_TREE_RESCAN_TIMEOUT_MS, 'graceful-stop rescan', !options.snapshot), [pid], ownerKey), deadline, 'graceful-stop rescan');
512
+ const root = snapshot.entries.find(entry => entry.pid === pid);
513
+ if (root && root.creationIdentity !== expectedRootCreationIdentity)
514
+ return false;
515
+ if (!snapshot.entries.length) {
516
+ stableEmpty += 1;
517
+ if (stableEmpty >= stableRequired)
518
+ return true;
519
+ continue;
520
+ }
521
+ // The root may have exited while a child was still observable through
522
+ // its parent PID. Add only the already-captured root identity so the
523
+ // normal identity/quiescence gate can safely terminate the survivor.
524
+ const captured = root
525
+ ? snapshot
526
+ : { rootPid: pid, entries: [syntheticRoot, ...snapshot.entries] };
527
+ await terminateCapturedWindowsProcessTree(captured, {
528
+ ...options,
529
+ helperOwnerKey: ownerKey,
530
+ forceStopDeadlineMs: Math.max(1, deadline - Date.now()),
531
+ });
532
+ return true;
533
+ }
534
+ catch {
535
+ return false;
536
+ }
537
+ }
538
+ return false;
539
+ }
476
540
  function remainingWindowsTreeBudget(deadline, capMs, label, reserveHelperClose = false) {
477
541
  const remaining = deadline - Date.now() - (reserveHelperClose ? WINDOWS_HELPER_CLOSE_GRACE_MS + 75 : 0);
478
542
  if (remaining < 100)
@@ -1014,6 +1078,20 @@ class ElectronUtilityAgentClient {
1014
1078
  if (exited && this.child === child)
1015
1079
  this.detachChild(child, new Error('Electron utility runtime stopped'));
1016
1080
  else if (!exited) {
1081
+ const rootIdentity = this.childRootIdentity;
1082
+ if (!this.restartQuarantine
1083
+ && rootIdentity
1084
+ && rootIdentity.generation === this.childGeneration
1085
+ && rootIdentity.pid === Number(child.pid || 0)
1086
+ && rootIdentity.creationIdentity) {
1087
+ const helperOwnerKey = this.windowsHelperOwnerKey(rootIdentity.generation);
1088
+ const quiescent = await confirmWindowsUtilityProcessTreeStopped(rootIdentity.pid, rootIdentity.creationIdentity, { ...this.options.windowsProcessTree, helperOwnerKey }, helperOwnerKey);
1089
+ if (quiescent) {
1090
+ if (this.child === child)
1091
+ this.detachChild(child, new Error('Electron utility runtime stopped'));
1092
+ return;
1093
+ }
1094
+ }
1017
1095
  this.enterRestartQuarantine(failure);
1018
1096
  // Retain the UtilityProcess object and surface the failure. The pool
1019
1097
  // must not evict the only identity-safe handle to a still-live child.
@@ -1071,8 +1149,12 @@ class ElectronUtilityAgentClient {
1071
1149
  // killing through it is identity-safe even when PID-tree discovery is
1072
1150
  // uncertain. Descendants remain unknown, hence quarantine is still
1073
1151
  // permanent and replacement remains forbidden.
1074
- const exited = await this.killChildHandleAndAwaitExit(child, 1_000);
1075
- if (exited && this.child === child)
1152
+ const rootAlreadyGone = process.platform === 'win32' && !processRootIsAlive(pid);
1153
+ const exited = await this.killChildHandleAndAwaitExit(child, 5_000);
1154
+ // The root handle can outlive the OS process after a tree proof fails.
1155
+ // Detach that dead root handle, but keep the sticky quarantine because
1156
+ // the failed tree proof still leaves descendant ownership unknown.
1157
+ if ((exited || rootAlreadyGone) && this.child === child)
1076
1158
  this.detachChild(child, failure);
1077
1159
  throw failure;
1078
1160
  }
@@ -2,6 +2,7 @@ import { BrowserControl } from './browserControl';
2
2
  import { BrowserUseReceipt, BrowserUseRequest } from './browserUse';
3
3
  import { UtilityHostToolRequest } from './utilityAgentProtocol';
4
4
  import { runComputerUse } from '../tools/computerUse';
5
+ import { ComputerUseSessionState } from './computerUseSession';
5
6
  import { runTerminalTakeover } from '../tools/terminalTakeover';
6
7
  export interface UtilityHostToolRouterOptions {
7
8
  persistenceRoot: string;
@@ -15,6 +16,12 @@ export interface UtilityHostToolRouterOptions {
15
16
  }
16
17
  export type RoutedUtilityHostToolHandler = ((request: UtilityHostToolRequest, signal?: AbortSignal) => Promise<unknown>) & {
17
18
  cancelTarget(runtimeKey: string): void;
19
+ computerUseState(runtimeKey: string): ComputerUseSessionState;
20
+ setComputerUseEnabled(runtimeKey: string, enabled: boolean, ownerLabel?: string): {
21
+ ok: boolean;
22
+ state: ComputerUseSessionState;
23
+ error?: string;
24
+ };
18
25
  };
19
26
  /**
20
27
  * Routes every desktop-global capability in the Electron main process.
@@ -39,6 +39,7 @@ const fs = __importStar(require("fs"));
39
39
  const browserUse_1 = require("./browserUse");
40
40
  const toolPolicy_1 = require("./toolPolicy");
41
41
  const computerUse_1 = require("../tools/computerUse");
42
+ const computerUseSession_1 = require("./computerUseSession");
42
43
  const terminalTakeover_1 = require("../tools/terminalTakeover");
43
44
  const ROOT_AGENT_ACTOR_ID = '00000000-0000-4000-8000-000000000001';
44
45
  const AUTOMATION_TOOLS = new Set([
@@ -54,10 +55,8 @@ const AUTOMATION_TOOLS = new Set([
54
55
  * one authoritative owner lock rather than one lock per child process.
55
56
  */
56
57
  function createUtilityHostToolHandler(options) {
57
- let computerUseLease = null;
58
58
  const terminalOwners = new Map();
59
59
  const ephemeralScreenshots = new Map();
60
- const lockTtlMs = 10 * 60 * 1000;
61
60
  const handler = async (request, signal) => {
62
61
  throwIfAborted(signal);
63
62
  validateTargetContext(request);
@@ -73,7 +72,14 @@ function createUtilityHostToolHandler(options) {
73
72
  if (request.tool === 'browser_control') {
74
73
  if (request.args.action === 'use')
75
74
  throw new Error('Isolated Browser-Use must use the target-bound browser_use host RPC');
76
- const result = await (options.runBrowser || browserControl_1.BrowserControl.run.bind(browserControl_1.BrowserControl))(request.args, signal);
75
+ const result = await (options.runBrowser || browserControl_1.BrowserControl.run.bind(browserControl_1.BrowserControl))({
76
+ ...request.args,
77
+ target: {
78
+ workspaceId: request.target.workspaceId,
79
+ conversationId: request.target.conversationId,
80
+ runtimeKey: request.target.runtimeKey,
81
+ },
82
+ }, signal);
77
83
  throwIfAborted(signal);
78
84
  return result;
79
85
  }
@@ -124,20 +130,14 @@ function createUtilityHostToolHandler(options) {
124
130
  const action = String(args.action || '').trim().toLowerCase();
125
131
  const trustedComputerUseContext = request.context;
126
132
  const owner = `${request.target.runtimeKey}:${String(request.context.actorId || terminalTakeover_1.ROOT_TERMINAL_ACTOR_ID)}`;
127
- const now = Date.now();
128
- if (computerUseLease && now - computerUseLease.updatedAt > lockTtlMs)
129
- computerUseLease = null;
130
- if (action === 'takeover_stop') {
131
- if (computerUseLease && computerUseLease.owner !== owner) {
132
- return computerUseLockError(action, owner, computerUseLease.owner);
133
- }
134
- }
135
- else if (computerUseLease && computerUseLease.owner !== owner) {
136
- return computerUseLockError(action, owner, computerUseLease.owner);
137
- }
138
- else {
139
- computerUseLease = { owner, runtimeKey: request.target.runtimeKey, workspacePath: request.target.workspacePath, updatedAt: now };
140
- }
133
+ const sessionScope = {
134
+ runtimeKey: request.target.runtimeKey,
135
+ ownerLabel: owner,
136
+ workspacePath: request.target.workspacePath,
137
+ };
138
+ const lockGuard = computerUseSession_1.defaultComputerUseSessionRegistry.authorize(action, sessionScope, args.dry_run === true || args.dryRun === true);
139
+ if (lockGuard)
140
+ return lockGuard;
141
141
  let retainedScreenshotPath = '';
142
142
  try {
143
143
  const result = await (options.runComputer || computerUse_1.runComputerUse)({
@@ -214,10 +214,7 @@ function createUtilityHostToolHandler(options) {
214
214
  }
215
215
  catch { }
216
216
  }
217
- if (action === 'takeover_stop' && (!computerUseLease || computerUseLease.owner === owner))
218
- computerUseLease = null;
219
- else if (computerUseLease?.owner === owner)
220
- computerUseLease.updatedAt = Date.now();
217
+ computerUseSession_1.defaultComputerUseSessionRegistry.complete(action, sessionScope);
221
218
  }
222
219
  };
223
220
  handler.cancelTarget = (runtimeKey) => {
@@ -238,9 +235,9 @@ function createUtilityHostToolHandler(options) {
238
235
  (0, terminalTakeover_1.stopTerminalTakeoverSession)(session.id, terminalOwner, 'runtime-force-restart');
239
236
  }
240
237
  }
241
- if (computerUseLease?.runtimeKey === runtimeKey) {
242
- const lease = computerUseLease;
243
- computerUseLease = null;
238
+ const hadComputerUseLease = computerUseSession_1.defaultComputerUseSessionRegistry.cancelTarget(runtimeKey);
239
+ if (hadComputerUseLease) {
240
+ const lease = { workspacePath: options.persistenceRoot, owner: runtimeKey };
244
241
  void (options.runComputer || computerUse_1.runComputerUse)({
245
242
  action: 'takeover_stop',
246
243
  workspacePath: lease.workspacePath,
@@ -249,6 +246,10 @@ function createUtilityHostToolHandler(options) {
249
246
  }).catch(() => undefined);
250
247
  }
251
248
  };
249
+ handler.computerUseState = (runtimeKey) => computerUseSession_1.defaultComputerUseSessionRegistry.state(runtimeKey);
250
+ handler.setComputerUseEnabled = (runtimeKey, enabled, ownerLabel = `conversation:${runtimeKey}`) => {
251
+ return computerUseSession_1.defaultComputerUseSessionRegistry.setEnabled({ runtimeKey, ownerLabel, workspacePath: options.persistenceRoot }, enabled);
252
+ };
252
253
  return handler;
253
254
  }
254
255
  function throwIfAborted(signal) {
@@ -307,13 +308,4 @@ function evaluateUtilityHostToolPolicy(request) {
307
308
  args,
308
309
  });
309
310
  }
310
- function computerUseLockError(action, requestedOwner, activeOwner) {
311
- return JSON.stringify({
312
- ok: false,
313
- action,
314
- error: `Computer Use is already active in ${activeOwner}. Stop it with computer_use takeover_stop or wait before another conversation takes control.`,
315
- lock_owner: activeOwner,
316
- requested_owner: requestedOwner,
317
- }, null, 2);
318
- }
319
311
  //# sourceMappingURL=utilityHostToolRouter.js.map
package/dist/launcher.js CHANGED
@@ -43,7 +43,12 @@ const agent_1 = require("./core/agent");
43
43
  const flow_1 = require("./core/flow");
44
44
  const flow_runner_1 = require("./core/flow-runner");
45
45
  const cli_commands_1 = require("./cli-commands");
46
+ const installUpdate_1 = require("./core/installUpdate");
47
+ const cli_help_1 = require("./cli-help");
46
48
  const args = process.argv.slice(2);
49
+ const hasCliCommand = args.some(a => cli_commands_1.CLI_COMMANDS.includes(a));
50
+ const isHelpArg = !hasCliCommand && (args.some(arg => ['--help', '-h'].includes(arg.toLowerCase())) || args[0]?.toLowerCase() === 'help');
51
+ const isVersionArg = !hasCliCommand && args.some(arg => ['--version', '-v'].includes(arg.toLowerCase()));
47
52
  const isTui = args.some(arg => arg.toLowerCase() === '--tui');
48
53
  const isGui = args.some(arg => arg.toLowerCase() === '--gui');
49
54
  const isCli = args.includes('--cli');
@@ -51,7 +56,6 @@ const isServer = args.includes('--server');
51
56
  const isEdit = args[0] === 'edit';
52
57
  const editFile = isEdit ? args[1] : '';
53
58
  const isFlow = args[0] === 'flow';
54
- const hasCliCommand = args.some(a => cli_commands_1.CLI_COMMANDS.includes(a));
55
59
  function pathArgValue(values, key) {
56
60
  const prefix = `${key}=`;
57
61
  const inlineIdx = values.findIndex(a => a.startsWith(prefix));
@@ -169,6 +173,14 @@ function writableRuntimeRoot(candidate) {
169
173
  }
170
174
  const explicitRoot = pathArgValue(args, '--root');
171
175
  const root = explicitRoot ? writableRuntimeRoot(explicitRoot) : userRuntimeRoot();
176
+ if (isHelpArg) {
177
+ console.log((0, cli_help_1.newmarkHelpText)((0, installUpdate_1.currentAppVersion)()));
178
+ process.exit(0);
179
+ }
180
+ if (isVersionArg) {
181
+ console.log((0, installUpdate_1.currentAppVersion)());
182
+ process.exit(0);
183
+ }
172
184
  function firstRunInit(r) {
173
185
  fs.mkdirSync(r, { recursive: true });
174
186
  migrateLegacyRuntimeRoot(r);