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.
- package/dist/cli-help.d.ts +2 -0
- package/dist/cli-help.js +22 -0
- package/dist/conversation-utility-host.bundle.cjs +191 -53
- package/dist/core/agent.js +22 -4
- package/dist/core/agentKernelRunner.js +19 -2
- package/dist/core/browserControl.d.ts +8 -0
- package/dist/core/browserUsePageAdapter.d.ts +3 -0
- package/dist/core/browserUsePageAdapter.js +19 -2
- package/dist/core/computerUseSession.d.ts +44 -0
- package/dist/core/computerUseSession.js +105 -0
- package/dist/core/conversationKernel.d.ts +1 -0
- package/dist/core/conversationKernel.js +38 -0
- package/dist/core/electronBrowserUseHost.js +7 -0
- package/dist/core/electronUtilityAgentClient.js +84 -2
- package/dist/core/utilityHostToolRouter.d.ts +7 -0
- package/dist/core/utilityHostToolRouter.js +25 -33
- package/dist/launcher.js +13 -1
- package/dist/main.js +132 -18
- package/dist/preload.js +4 -2
- package/dist/tools/index.js +36 -49
- package/dist/ui/index.html +296 -39
- package/dist/wsl-agent-host.bundle.cjs +191 -53
- package/package.json +2 -2
package/dist/cli-help.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.newmarkHelpText = newmarkHelpText;
|
|
4
|
+
const cli_commands_1 = require("./cli-commands");
|
|
5
|
+
function newmarkHelpText(version) {
|
|
6
|
+
return [
|
|
7
|
+
`Newmark Agent ${version}`,
|
|
8
|
+
'',
|
|
9
|
+
'Usage:',
|
|
10
|
+
' Newmark Agent.exe [--gui|--TUI|--cli] [--root <dir>]',
|
|
11
|
+
' Newmark.exe <command> ...',
|
|
12
|
+
' Newmark.exe flow <workflow-name> [start-pc] [--input "text"] [--root <dir>]',
|
|
13
|
+
' Newmark.exe edit <file.txt|.json|.tex|.md>',
|
|
14
|
+
'',
|
|
15
|
+
(0, cli_commands_1.cliCommandUsage)(),
|
|
16
|
+
'',
|
|
17
|
+
'Flags:',
|
|
18
|
+
' --help, -h Show this help and exit.',
|
|
19
|
+
' --version, -v Show the installed version and exit.',
|
|
20
|
+
].join('\n');
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=cli-help.js.map
|
|
@@ -335769,10 +335769,102 @@ function browserVisualFallback(runtimeKey, observationId) {
|
|
|
335769
335769
|
return browserFallbacks.get(key(runtimeKey, observationId)) || null;
|
|
335770
335770
|
}
|
|
335771
335771
|
|
|
335772
|
+
// src/core/computerUseSession.ts
|
|
335773
|
+
var COMPUTER_USE_OCCUPIED_MARKER = "computerUse occupied";
|
|
335774
|
+
var COMPUTER_USE_LOCK_TTL_MS = 10 * 60 * 1e3;
|
|
335775
|
+
var ComputerUseSessionRegistry = class {
|
|
335776
|
+
constructor(ttlMs = COMPUTER_USE_LOCK_TTL_MS) {
|
|
335777
|
+
this.ttlMs = ttlMs;
|
|
335778
|
+
}
|
|
335779
|
+
ttlMs;
|
|
335780
|
+
enabledByRuntime = /* @__PURE__ */ new Map();
|
|
335781
|
+
activeLease = null;
|
|
335782
|
+
authorize(action, scope, dryRun = false) {
|
|
335783
|
+
const normalizedAction = String(action || "").trim().toLowerCase();
|
|
335784
|
+
const runtimeKey = String(scope.runtimeKey || "").trim() || "conversation:default";
|
|
335785
|
+
const now2 = Date.now();
|
|
335786
|
+
this.clearExpired(now2);
|
|
335787
|
+
if (this.activeLease && this.activeLease.runtimeKey !== runtimeKey) {
|
|
335788
|
+
return this.occupiedError(normalizedAction, scope.ownerLabel, this.activeLease.ownerLabel);
|
|
335789
|
+
}
|
|
335790
|
+
if (normalizedAction === "takeover_stop") return null;
|
|
335791
|
+
const enabled = this.enabledByRuntime.get(runtimeKey) !== false;
|
|
335792
|
+
const readOnly = normalizedAction === "observe" || normalizedAction === "app_list" || normalizedAction === "app_observe" || normalizedAction === "wait";
|
|
335793
|
+
if (!enabled && !readOnly && !dryRun && normalizedAction !== "takeover_start") {
|
|
335794
|
+
return JSON.stringify({
|
|
335795
|
+
ok: false,
|
|
335796
|
+
action: normalizedAction,
|
|
335797
|
+
error: `ComputerUse is disabled for ${scope.ownerLabel}. Enable ComputerUse for this conversation before sending desktop operations.`,
|
|
335798
|
+
computer_use_enabled: false,
|
|
335799
|
+
requested_owner: scope.ownerLabel
|
|
335800
|
+
}, null, 2);
|
|
335801
|
+
}
|
|
335802
|
+
if (normalizedAction === "takeover_start") this.enabledByRuntime.set(runtimeKey, true);
|
|
335803
|
+
if (!this.activeLease) {
|
|
335804
|
+
this.activeLease = { ...scope, runtimeKey, updatedAt: now2 };
|
|
335805
|
+
} else {
|
|
335806
|
+
this.activeLease.updatedAt = now2;
|
|
335807
|
+
}
|
|
335808
|
+
return null;
|
|
335809
|
+
}
|
|
335810
|
+
complete(action, scope) {
|
|
335811
|
+
const normalizedAction = String(action || "").trim().toLowerCase();
|
|
335812
|
+
const runtimeKey = String(scope.runtimeKey || "").trim() || "conversation:default";
|
|
335813
|
+
if (normalizedAction === "takeover_stop") {
|
|
335814
|
+
if (!this.activeLease || this.activeLease.runtimeKey === runtimeKey) this.activeLease = null;
|
|
335815
|
+
this.enabledByRuntime.set(runtimeKey, false);
|
|
335816
|
+
return;
|
|
335817
|
+
}
|
|
335818
|
+
if (this.activeLease?.runtimeKey === runtimeKey) this.activeLease.updatedAt = Date.now();
|
|
335819
|
+
}
|
|
335820
|
+
setEnabled(scope, enabled) {
|
|
335821
|
+
const runtimeKey = String(scope.runtimeKey || "").trim() || "conversation:default";
|
|
335822
|
+
this.clearExpired();
|
|
335823
|
+
if (this.activeLease && this.activeLease.runtimeKey !== runtimeKey) {
|
|
335824
|
+
return { ok: false, error: this.occupiedError("toggle", scope.ownerLabel, this.activeLease.ownerLabel), state: this.state(runtimeKey) };
|
|
335825
|
+
}
|
|
335826
|
+
this.enabledByRuntime.set(runtimeKey, enabled !== false);
|
|
335827
|
+
if (enabled === false && this.activeLease?.runtimeKey === runtimeKey) this.activeLease = null;
|
|
335828
|
+
return { ok: true, state: this.state(runtimeKey) };
|
|
335829
|
+
}
|
|
335830
|
+
state(runtimeKey) {
|
|
335831
|
+
const key3 = String(runtimeKey || "").trim() || "conversation:default";
|
|
335832
|
+
this.clearExpired();
|
|
335833
|
+
const lease = this.activeLease;
|
|
335834
|
+
return {
|
|
335835
|
+
runtimeKey: key3,
|
|
335836
|
+
enabled: this.enabledByRuntime.get(key3) !== false,
|
|
335837
|
+
occupied: !!lease,
|
|
335838
|
+
...lease ? { ownerLabel: lease.ownerLabel, updatedAt: lease.updatedAt } : {}
|
|
335839
|
+
};
|
|
335840
|
+
}
|
|
335841
|
+
cancelTarget(runtimeKey) {
|
|
335842
|
+
const key3 = String(runtimeKey || "").trim();
|
|
335843
|
+
if (!key3) return false;
|
|
335844
|
+
const hadActiveLease = this.activeLease?.runtimeKey === key3;
|
|
335845
|
+
if (hadActiveLease) this.activeLease = null;
|
|
335846
|
+
this.enabledByRuntime.set(key3, false);
|
|
335847
|
+
return hadActiveLease;
|
|
335848
|
+
}
|
|
335849
|
+
clearExpired(now2 = Date.now()) {
|
|
335850
|
+
if (this.activeLease && now2 - this.activeLease.updatedAt > this.ttlMs) {
|
|
335851
|
+
this.activeLease = null;
|
|
335852
|
+
}
|
|
335853
|
+
}
|
|
335854
|
+
occupiedError(action, requestedOwner, activeOwner) {
|
|
335855
|
+
return JSON.stringify({
|
|
335856
|
+
ok: false,
|
|
335857
|
+
action,
|
|
335858
|
+
error: `${COMPUTER_USE_OCCUPIED_MARKER}: ComputerUse is already active in ${activeOwner}. Stop it with computer_use takeover_stop or wait before another conversation takes control.`,
|
|
335859
|
+
lock_owner: activeOwner,
|
|
335860
|
+
requested_owner: requestedOwner
|
|
335861
|
+
}, null, 2);
|
|
335862
|
+
}
|
|
335863
|
+
};
|
|
335864
|
+
var defaultComputerUseSessionRegistry = new ComputerUseSessionRegistry();
|
|
335865
|
+
|
|
335772
335866
|
// src/tools/index.ts
|
|
335773
335867
|
var globSync = require_index_min().sync;
|
|
335774
|
-
var computerUseLock = null;
|
|
335775
|
-
var COMPUTER_USE_LOCK_TTL_MS = 10 * 60 * 1e3;
|
|
335776
335868
|
function normalizeComputerUseAction(action) {
|
|
335777
335869
|
return String(action || "").trim().toLowerCase();
|
|
335778
335870
|
}
|
|
@@ -335857,49 +335949,24 @@ async function abortableToolDelay(durationMs, signal) {
|
|
|
335857
335949
|
if (signal?.aborted) abort();
|
|
335858
335950
|
});
|
|
335859
335951
|
}
|
|
335860
|
-
function
|
|
335861
|
-
|
|
335862
|
-
|
|
335863
|
-
|
|
335864
|
-
|
|
335865
|
-
function computerUseLockError(action, owner) {
|
|
335866
|
-
return JSON.stringify({
|
|
335867
|
-
ok: false,
|
|
335868
|
-
action,
|
|
335869
|
-
error: `ComputerUse is already active in ${computerUseLock?.owner || "another conversation"}. Stop it with computer_use takeover_stop or wait before using ComputerUse from another conversation.`,
|
|
335870
|
-
lock_owner: computerUseLock?.owner || "",
|
|
335871
|
-
requested_owner: owner
|
|
335872
|
-
}, null, 2);
|
|
335873
|
-
}
|
|
335874
|
-
function acquireComputerUseLock(action, owner, wsPath) {
|
|
335875
|
-
const now2 = Date.now();
|
|
335876
|
-
clearStaleComputerUseLock(now2);
|
|
335877
|
-
if (computerUseLock && computerUseLock.owner !== owner) {
|
|
335878
|
-
return computerUseLockError(action, owner);
|
|
335879
|
-
}
|
|
335880
|
-
computerUseLock = {
|
|
335881
|
-
owner,
|
|
335882
|
-
workspacePath: path15.resolve(wsPath || process.cwd()),
|
|
335883
|
-
acquiredAt: computerUseLock?.owner === owner ? computerUseLock.acquiredAt : now2,
|
|
335884
|
-
updatedAt: now2
|
|
335952
|
+
function computerUseSessionScope(context, wsPath, owner) {
|
|
335953
|
+
return {
|
|
335954
|
+
runtimeKey: browserUseScope(context, wsPath).runtimeKey,
|
|
335955
|
+
ownerLabel: owner,
|
|
335956
|
+
workspacePath: path15.resolve(wsPath || process.cwd())
|
|
335885
335957
|
};
|
|
335886
|
-
return null;
|
|
335887
335958
|
}
|
|
335888
|
-
function
|
|
335889
|
-
|
|
335890
|
-
if (computerUseLock && computerUseLock.owner !== owner) {
|
|
335891
|
-
return computerUseLockError(action, owner);
|
|
335892
|
-
}
|
|
335893
|
-
if (computerUseLock?.owner === owner) computerUseLock = null;
|
|
335894
|
-
return null;
|
|
335959
|
+
function acquireComputerUseLock(action, owner, wsPath, context = {}, dryRun = false) {
|
|
335960
|
+
return defaultComputerUseSessionRegistry.authorize(action, computerUseSessionScope(context, wsPath, owner), dryRun);
|
|
335895
335961
|
}
|
|
335896
|
-
function
|
|
335897
|
-
|
|
335898
|
-
|
|
335899
|
-
return computerUseLockError(action, owner);
|
|
335900
|
-
}
|
|
335962
|
+
function releaseComputerUseLock(action, owner, context = {}, wsPath = context.workspacePath || "") {
|
|
335963
|
+
const scope = computerUseSessionScope(context, wsPath, owner);
|
|
335964
|
+
defaultComputerUseSessionRegistry.complete(action, scope);
|
|
335901
335965
|
return null;
|
|
335902
335966
|
}
|
|
335967
|
+
function assertComputerUseLockOwner(action, owner, context = {}, wsPath = context.workspacePath || "") {
|
|
335968
|
+
return defaultComputerUseSessionRegistry.authorize(action, computerUseSessionScope(context, wsPath, owner));
|
|
335969
|
+
}
|
|
335903
335970
|
var ToolExecutor = class {
|
|
335904
335971
|
constructor(root2, config, ssh, workspace) {
|
|
335905
335972
|
this.config = config;
|
|
@@ -336450,7 +336517,7 @@ var ToolExecutor = class {
|
|
|
336450
336517
|
case "computer_use": {
|
|
336451
336518
|
const action = normalizeComputerUseAction(g2("action"));
|
|
336452
336519
|
const owner = `${computerUseOwner(context, wsPath)}:${String(context.actorId || "root")}`;
|
|
336453
|
-
const lockGuard = action === "takeover_stop" ? assertComputerUseLockOwner(action, owner) : acquireComputerUseLock(action, owner, wsPath);
|
|
336520
|
+
const lockGuard = action === "takeover_stop" ? assertComputerUseLockOwner(action, owner, context, wsPath) : acquireComputerUseLock(action, owner, wsPath, context, args.dry_run === true);
|
|
336454
336521
|
if (lockGuard) return lockGuard;
|
|
336455
336522
|
if (process.env.NEWMARK_WSL_DISTRO) {
|
|
336456
336523
|
try {
|
|
@@ -336464,7 +336531,7 @@ var ToolExecutor = class {
|
|
|
336464
336531
|
}, 12e4, context.signal);
|
|
336465
336532
|
return typeof result === "string" ? result : JSON.stringify(result);
|
|
336466
336533
|
} finally {
|
|
336467
|
-
if (action === "takeover_stop") releaseComputerUseLock(action, owner);
|
|
336534
|
+
if (action === "takeover_stop") releaseComputerUseLock(action, owner, context, wsPath);
|
|
336468
336535
|
}
|
|
336469
336536
|
}
|
|
336470
336537
|
if (process.env.NEWMARK_ISOLATED_RUNTIME === "1") {
|
|
@@ -336481,7 +336548,7 @@ var ToolExecutor = class {
|
|
|
336481
336548
|
const result = await requestUtilityHostTool("computer_use", args, trustedComputerUseContext, 12e4, context.signal);
|
|
336482
336549
|
return typeof result === "string" ? result : JSON.stringify(result);
|
|
336483
336550
|
} finally {
|
|
336484
|
-
if (action === "takeover_stop") releaseComputerUseLock(action, owner);
|
|
336551
|
+
if (action === "takeover_stop") releaseComputerUseLock(action, owner, context, wsPath);
|
|
336485
336552
|
}
|
|
336486
336553
|
}
|
|
336487
336554
|
const output = await runComputerUse({
|
|
@@ -336522,7 +336589,7 @@ var ToolExecutor = class {
|
|
|
336522
336589
|
durationMs: Number(step.duration_ms || 0)
|
|
336523
336590
|
})) : void 0
|
|
336524
336591
|
});
|
|
336525
|
-
if (action === "takeover_stop") releaseComputerUseLock(action, owner);
|
|
336592
|
+
if (action === "takeover_stop") releaseComputerUseLock(action, owner, context, wsPath);
|
|
336526
336593
|
return output;
|
|
336527
336594
|
}
|
|
336528
336595
|
case "terminal_takeover": {
|
|
@@ -337003,9 +337070,17 @@ ${snippet ? clean(snippet[1]) : ""}`.trim());
|
|
|
337003
337070
|
}
|
|
337004
337071
|
}
|
|
337005
337072
|
async browserRun(request, signal, context = {}, workspacePath = this.root) {
|
|
337073
|
+
const scope = browserUseScope(context, workspacePath);
|
|
337074
|
+
const scopedRequest = {
|
|
337075
|
+
...request,
|
|
337076
|
+
target: {
|
|
337077
|
+
workspaceId: context.workspaceId || terminalTakeoverWorkspaceId(workspacePath),
|
|
337078
|
+
conversationId: context.conversationId || "default",
|
|
337079
|
+
runtimeKey: scope.runtimeKey
|
|
337080
|
+
}
|
|
337081
|
+
};
|
|
337006
337082
|
if (process.env.NEWMARK_WSL_DISTRO) {
|
|
337007
|
-
const
|
|
337008
|
-
const result2 = await requestWindowsHostTool("browser_control", request, {
|
|
337083
|
+
const result2 = await requestWindowsHostTool("browser_control", scopedRequest, {
|
|
337009
337084
|
conversationId: context.conversationId || process.env.NEWMARK_CONVERSATION_ID || "default",
|
|
337010
337085
|
workspaceId: process.env.NEWMARK_WORKSPACE_ID || context.workspaceId || terminalTakeoverWorkspaceId(workspacePath),
|
|
337011
337086
|
actorId: context.actorId || ROOT_TERMINAL_ACTOR_ID,
|
|
@@ -337015,10 +337090,10 @@ ${snippet ? clean(snippet[1]) : ""}`.trim());
|
|
|
337015
337090
|
return this.formatBrowserResult(result2);
|
|
337016
337091
|
}
|
|
337017
337092
|
if (process.env.NEWMARK_ISOLATED_RUNTIME === "1") {
|
|
337018
|
-
const result2 = await requestUtilityHostTool("browser_control",
|
|
337093
|
+
const result2 = await requestUtilityHostTool("browser_control", scopedRequest, void 0, 3e4, signal);
|
|
337019
337094
|
return this.formatBrowserResult(result2);
|
|
337020
337095
|
}
|
|
337021
|
-
const result = await BrowserControl.run(
|
|
337096
|
+
const result = await BrowserControl.run(scopedRequest, signal);
|
|
337022
337097
|
return this.formatBrowserResult(result);
|
|
337023
337098
|
}
|
|
337024
337099
|
formatBrowserResult(result) {
|
|
@@ -339896,6 +339971,10 @@ var ProviderRunError = class extends Error {
|
|
|
339896
339971
|
function kernelTurnFailed(agent, turn) {
|
|
339897
339972
|
return turn.stopReason === "error" || agent.isLlmErrorText(turn.text);
|
|
339898
339973
|
}
|
|
339974
|
+
function providerTurnIsEmpty(turn) {
|
|
339975
|
+
return /provider returned an empty response/i.test(`${turn.errorMessage}
|
|
339976
|
+
${turn.text}`);
|
|
339977
|
+
}
|
|
339899
339978
|
function removeTrailingFailedAssistant(agent, messages) {
|
|
339900
339979
|
const last = messages[messages.length - 1];
|
|
339901
339980
|
if (last?.role !== "assistant") return;
|
|
@@ -340007,10 +340086,13 @@ async function runAgentKernel(agent) {
|
|
|
340007
340086
|
}
|
|
340008
340087
|
await kernel2.prompt(promptMessages);
|
|
340009
340088
|
const assistant = lastAssistant;
|
|
340089
|
+
const text = assistant ? KernelMessageText(assistant) : "";
|
|
340090
|
+
const hasToolCall = !!assistant?.content?.some((content) => content.type === "toolCall");
|
|
340091
|
+
const emptyResponse = !assistant || !text.trim() && !hasToolCall && String(assistant?.stopReason || "") !== "aborted";
|
|
340010
340092
|
return {
|
|
340011
|
-
text:
|
|
340093
|
+
text: emptyResponse ? "[Error] Provider returned an empty response." : text,
|
|
340012
340094
|
stopReason: String(assistant?.stopReason || ""),
|
|
340013
|
-
errorMessage: String(assistant?.errorMessage || "")
|
|
340095
|
+
errorMessage: String(assistant?.errorMessage || (emptyResponse ? "Provider returned an empty response." : ""))
|
|
340014
340096
|
};
|
|
340015
340097
|
} finally {
|
|
340016
340098
|
unsubscribe();
|
|
@@ -340045,6 +340127,16 @@ async function runAgentKernel(agent) {
|
|
|
340045
340127
|
if (modelBeforeKernelRun && modelBeforeKernelRun !== agent.model && !tokens.some((t3) => t3.text?.includes("[Model fallback]"))) {
|
|
340046
340128
|
tokens.unshift({ type: "text", text: `[Model fallback] ${modelBeforeKernelRun} unavailable; switched to ${agent.model}.` });
|
|
340047
340129
|
}
|
|
340130
|
+
let emptyResponseRetries = 0;
|
|
340131
|
+
while (providerTurnIsEmpty(lastTurn) && emptyResponseRetries < 2) {
|
|
340132
|
+
removeTrailingFailedAssistant(agent, kernel2.state.messages);
|
|
340133
|
+
emptyResponseRetries += 1;
|
|
340134
|
+
const notice = `[Model retry] Provider returned an empty response; retrying the same deployment (${emptyResponseRetries}/2).`;
|
|
340135
|
+
tokens.push({ type: "text", text: notice });
|
|
340136
|
+
agent.recordWorkStatus(notice);
|
|
340137
|
+
await agent.waitForPlannedRouteRetry();
|
|
340138
|
+
lastTurn = await runWithCompressionResume([], false);
|
|
340139
|
+
}
|
|
340048
340140
|
let routeRetries = 0;
|
|
340049
340141
|
while (kernelTurnFailed(agent, lastTurn) && routeRetries < 2) {
|
|
340050
340142
|
const previous = agent.switchToFallbackModel(lastTurn.errorMessage || lastTurn.text);
|
|
@@ -351075,8 +351167,26 @@ var GoalStateImpl = class {
|
|
|
351075
351167
|
return s3;
|
|
351076
351168
|
}
|
|
351077
351169
|
checkComplete(response) {
|
|
351078
|
-
const
|
|
351079
|
-
|
|
351170
|
+
const lines = String(response || "").replace(/\r\n?/g, "\n").split("\n");
|
|
351171
|
+
const completionMarkers = "(?:goal\\s+complete|objective\\s+achieved|task\\s+finished|all\\s+done|goal\\s+accomplished)";
|
|
351172
|
+
const explicitLinePatterns = [
|
|
351173
|
+
new RegExp(`^\\s*(?:[*#_~\\-]+\\s*)*(?:\\[\\s*)?${completionMarkers}(?:\\s*\\])?(?=\\s|[!.,:;]|$)`, "i"),
|
|
351174
|
+
/^\s*(?:[*#_~\-]+\s*)*(?:i|we)\s+(?:have\s+)?(?:now\s+)?(?:fully\s+)?(?:completed|finished|achieved|accomplished)\s+(?:the\s+)?(?:goal|objective|task)\b/i,
|
|
351175
|
+
/^\s*(?:[*#_~\-]+\s*)*(?:the\s+)?(?:goal|objective|task)\s+(?:is|was)\s+(?:now\s+)?(?:complete|achieved|finished|accomplished)\b/i
|
|
351176
|
+
];
|
|
351177
|
+
const completionContext = new RegExp(completionMarkers, "i");
|
|
351178
|
+
const deferredBefore = new RegExp(`\\b(?:must|will|should|would|can|could)\\s+(?:still\\s+)?(?:contain(?:s|ed)?|include(?:s|d)?|say|state|use|write|appear)\\b.{0,180}${completionMarkers}`, "i");
|
|
351179
|
+
const negatedBefore = new RegExp(`\\b(?:not|isn't|is not|never|don't|do not|won't|will not)\\b.{0,180}${completionMarkers}`, "i");
|
|
351180
|
+
const pendingBefore = new RegExp(`\\b(?:one\\s+step\\s+remains|still\\s+required|one\\s+more\\s+(?:step|call|turn)|not\\s+(?:the\\s+)?final)\\b.{0,180}${completionMarkers}`, "i");
|
|
351181
|
+
const deferredAfter = new RegExp(`${completionMarkers}.{0,180}\\b(?:not\\s+(?:the\\s+)?final|not\\s+yet|one\\s+more\\s+model\\s+call|next\\s+(?:call|step)|not\\s+done)\\b`, "i");
|
|
351182
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
351183
|
+
const line = lines[index];
|
|
351184
|
+
if (!explicitLinePatterns.some((pattern) => pattern.test(line))) continue;
|
|
351185
|
+
const context = lines.slice(Math.max(0, index - 2), Math.min(lines.length, index + 3)).join(" ");
|
|
351186
|
+
if (completionContext.test(context) && (deferredBefore.test(context) || negatedBefore.test(context) || pendingBefore.test(context) || deferredAfter.test(context))) continue;
|
|
351187
|
+
return true;
|
|
351188
|
+
}
|
|
351189
|
+
return false;
|
|
351080
351190
|
}
|
|
351081
351191
|
};
|
|
351082
351192
|
|
|
@@ -351273,6 +351383,7 @@ var ConversationKernel = class {
|
|
|
351273
351383
|
runtime.runner.recordGuideReceipt(deferred2);
|
|
351274
351384
|
this.emitQueueUpdate(runtime);
|
|
351275
351385
|
this.activateAcceptedGoal(runtime, envelope.goalObjective);
|
|
351386
|
+
this.schedulePendingRuntimeContinuation(runtime, runtime.runId);
|
|
351276
351387
|
return deferred2;
|
|
351277
351388
|
}
|
|
351278
351389
|
if (runtime.guideAcceptanceClosedRunId === runtime.runId) {
|
|
@@ -351304,6 +351415,7 @@ var ConversationKernel = class {
|
|
|
351304
351415
|
createdAt: deferred2.createdAt
|
|
351305
351416
|
}]);
|
|
351306
351417
|
this.activateAcceptedGoal(runtime, envelope.goalObjective);
|
|
351418
|
+
this.schedulePendingRuntimeContinuation(runtime, runtime.runId);
|
|
351307
351419
|
return deferred2;
|
|
351308
351420
|
}
|
|
351309
351421
|
const queued = runtime.runner.queueActiveKernelMessage(safeEnvelope.text, "steer", clientMessageId, runtime.runId, safeEnvelope.images);
|
|
@@ -351334,6 +351446,7 @@ var ConversationKernel = class {
|
|
|
351334
351446
|
createdAt: deferred.createdAt
|
|
351335
351447
|
}]);
|
|
351336
351448
|
this.activateAcceptedGoal(runtime, envelope.goalObjective);
|
|
351449
|
+
this.schedulePendingRuntimeContinuation(runtime, runtime.runId);
|
|
351337
351450
|
return deferred;
|
|
351338
351451
|
}
|
|
351339
351452
|
checkpoint(target) {
|
|
@@ -351541,6 +351654,8 @@ var ConversationKernel = class {
|
|
|
351541
351654
|
if (runtime.stopRequestedRunId === runId) {
|
|
351542
351655
|
stopped = true;
|
|
351543
351656
|
this.settleCooperativeStop(runtime, runId);
|
|
351657
|
+
} else if (runtime.pendingNextTurn.length > 0) {
|
|
351658
|
+
this.schedulePendingRuntimeContinuation(runtime, runId);
|
|
351544
351659
|
}
|
|
351545
351660
|
}
|
|
351546
351661
|
}
|
|
@@ -351675,7 +351790,8 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
351675
351790
|
guideAcceptanceClosedRunId: "",
|
|
351676
351791
|
guideReceipts: /* @__PURE__ */ new Map(),
|
|
351677
351792
|
guideEnvelopes: /* @__PURE__ */ new Map(),
|
|
351678
|
-
goalContinuationTimer: void 0
|
|
351793
|
+
goalContinuationTimer: void 0,
|
|
351794
|
+
pendingContinuationRunId: void 0
|
|
351679
351795
|
};
|
|
351680
351796
|
runner.setGoalContinuationGate(() => {
|
|
351681
351797
|
this.queueState(runtime);
|
|
@@ -351747,6 +351863,28 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
351747
351863
|
});
|
|
351748
351864
|
}, 250);
|
|
351749
351865
|
}
|
|
351866
|
+
schedulePendingRuntimeContinuation(runtime, runId) {
|
|
351867
|
+
if (runtime.pendingContinuationRunId === runId) return;
|
|
351868
|
+
runtime.pendingContinuationRunId = runId;
|
|
351869
|
+
const active = runtime.activePromise;
|
|
351870
|
+
if (active) {
|
|
351871
|
+
const continueAfterSettlement = () => {
|
|
351872
|
+
runtime.pendingContinuationRunId = void 0;
|
|
351873
|
+
this.schedulePendingRuntimeContinuation(runtime, runId);
|
|
351874
|
+
};
|
|
351875
|
+
void active.then(continueAfterSettlement, continueAfterSettlement);
|
|
351876
|
+
return;
|
|
351877
|
+
}
|
|
351878
|
+
setImmediate(() => {
|
|
351879
|
+
runtime.pendingContinuationRunId = void 0;
|
|
351880
|
+
if (runtime.runId !== runId || runtime.activePromise || runtime.stopRequestedRunId === runId) return;
|
|
351881
|
+
const next = runtime.pendingNextTurn.shift();
|
|
351882
|
+
if (!next) return;
|
|
351883
|
+
const message = typeof next.message === "string" ? { text: next.message, runId } : { ...next.message, runId: next.message.runId || runId };
|
|
351884
|
+
void this.prompt(message, runtime.target, runtime.options, next.queueMode).catch(() => {
|
|
351885
|
+
});
|
|
351886
|
+
});
|
|
351887
|
+
}
|
|
351750
351888
|
startGoalDrivenBuild(runtime) {
|
|
351751
351889
|
if (runtime.goalContinuationTimer) {
|
|
351752
351890
|
clearTimeout(runtime.goalContinuationTimer);
|
package/dist/core/agent.js
CHANGED
|
@@ -8045,10 +8045,28 @@ class GoalStateImpl {
|
|
|
8045
8045
|
return s;
|
|
8046
8046
|
}
|
|
8047
8047
|
checkComplete(response) {
|
|
8048
|
-
const
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
|
|
8048
|
+
const lines = String(response || '').replace(/\r\n?/g, '\n').split('\n');
|
|
8049
|
+
const completionMarkers = '(?:goal\\s+complete|objective\\s+achieved|task\\s+finished|all\\s+done|goal\\s+accomplished)';
|
|
8050
|
+
const explicitLinePatterns = [
|
|
8051
|
+
new RegExp(`^\\s*(?:[*#_~\\-]+\\s*)*(?:\\[\\s*)?${completionMarkers}(?:\\s*\\])?(?=\\s|[!.,:;]|$)`, 'i'),
|
|
8052
|
+
/^\s*(?:[*#_~\-]+\s*)*(?:i|we)\s+(?:have\s+)?(?:now\s+)?(?:fully\s+)?(?:completed|finished|achieved|accomplished)\s+(?:the\s+)?(?:goal|objective|task)\b/i,
|
|
8053
|
+
/^\s*(?:[*#_~\-]+\s*)*(?:the\s+)?(?:goal|objective|task)\s+(?:is|was)\s+(?:now\s+)?(?:complete|achieved|finished|accomplished)\b/i,
|
|
8054
|
+
];
|
|
8055
|
+
const completionContext = new RegExp(completionMarkers, 'i');
|
|
8056
|
+
const deferredBefore = new RegExp(`\\b(?:must|will|should|would|can|could)\\s+(?:still\\s+)?(?:contain(?:s|ed)?|include(?:s|d)?|say|state|use|write|appear)\\b.{0,180}${completionMarkers}`, 'i');
|
|
8057
|
+
const negatedBefore = new RegExp(`\\b(?:not|isn't|is not|never|don't|do not|won't|will not)\\b.{0,180}${completionMarkers}`, 'i');
|
|
8058
|
+
const pendingBefore = new RegExp(`\\b(?:one\\s+step\\s+remains|still\\s+required|one\\s+more\\s+(?:step|call|turn)|not\\s+(?:the\\s+)?final)\\b.{0,180}${completionMarkers}`, 'i');
|
|
8059
|
+
const deferredAfter = new RegExp(`${completionMarkers}.{0,180}\\b(?:not\\s+(?:the\\s+)?final|not\\s+yet|one\\s+more\\s+model\\s+call|next\\s+(?:call|step)|not\\s+done)\\b`, 'i');
|
|
8060
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
8061
|
+
const line = lines[index];
|
|
8062
|
+
if (!explicitLinePatterns.some(pattern => pattern.test(line)))
|
|
8063
|
+
continue;
|
|
8064
|
+
const context = lines.slice(Math.max(0, index - 2), Math.min(lines.length, index + 3)).join(' ');
|
|
8065
|
+
if (completionContext.test(context) && (deferredBefore.test(context) || negatedBefore.test(context) || pendingBefore.test(context) || deferredAfter.test(context)))
|
|
8066
|
+
continue;
|
|
8067
|
+
return true;
|
|
8068
|
+
}
|
|
8069
|
+
return false;
|
|
8052
8070
|
}
|
|
8053
8071
|
}
|
|
8054
8072
|
//# sourceMappingURL=agent.js.map
|
|
@@ -205,6 +205,9 @@ class ProviderRunError extends Error {
|
|
|
205
205
|
function kernelTurnFailed(agent, turn) {
|
|
206
206
|
return turn.stopReason === 'error' || agent.isLlmErrorText(turn.text);
|
|
207
207
|
}
|
|
208
|
+
function providerTurnIsEmpty(turn) {
|
|
209
|
+
return /provider returned an empty response/i.test(`${turn.errorMessage}\n${turn.text}`);
|
|
210
|
+
}
|
|
208
211
|
function removeTrailingFailedAssistant(agent, messages) {
|
|
209
212
|
const last = messages[messages.length - 1];
|
|
210
213
|
if (last?.role !== 'assistant')
|
|
@@ -338,10 +341,14 @@ async function runAgentKernel(agent) {
|
|
|
338
341
|
}
|
|
339
342
|
await kernel.prompt(promptMessages);
|
|
340
343
|
const assistant = lastAssistant;
|
|
344
|
+
const text = assistant ? KernelMessageText(assistant) : '';
|
|
345
|
+
const hasToolCall = !!assistant?.content?.some(content => content.type === 'toolCall');
|
|
346
|
+
const emptyResponse = !assistant
|
|
347
|
+
|| (!text.trim() && !hasToolCall && String(assistant?.stopReason || '') !== 'aborted');
|
|
341
348
|
return {
|
|
342
|
-
text:
|
|
349
|
+
text: emptyResponse ? '[Error] Provider returned an empty response.' : text,
|
|
343
350
|
stopReason: String(assistant?.stopReason || ''),
|
|
344
|
-
errorMessage: String(assistant?.errorMessage || ''),
|
|
351
|
+
errorMessage: String(assistant?.errorMessage || (emptyResponse ? 'Provider returned an empty response.' : '')),
|
|
345
352
|
};
|
|
346
353
|
}
|
|
347
354
|
finally {
|
|
@@ -380,6 +387,16 @@ async function runAgentKernel(agent) {
|
|
|
380
387
|
if (modelBeforeKernelRun && modelBeforeKernelRun !== agent.model && !tokens.some(t => t.text?.includes('[Model fallback]'))) {
|
|
381
388
|
tokens.unshift({ type: 'text', text: `[Model fallback] ${modelBeforeKernelRun} unavailable; switched to ${agent.model}.` });
|
|
382
389
|
}
|
|
390
|
+
let emptyResponseRetries = 0;
|
|
391
|
+
while (providerTurnIsEmpty(lastTurn) && emptyResponseRetries < 2) {
|
|
392
|
+
removeTrailingFailedAssistant(agent, kernel.state.messages);
|
|
393
|
+
emptyResponseRetries += 1;
|
|
394
|
+
const notice = `[Model retry] Provider returned an empty response; retrying the same deployment (${emptyResponseRetries}/2).`;
|
|
395
|
+
tokens.push({ type: 'text', text: notice });
|
|
396
|
+
agent.recordWorkStatus(notice);
|
|
397
|
+
await agent.waitForPlannedRouteRetry();
|
|
398
|
+
lastTurn = await runWithCompressionResume([], false);
|
|
399
|
+
}
|
|
383
400
|
let routeRetries = 0;
|
|
384
401
|
while (kernelTurnFailed(agent, lastTurn) && routeRetries < 2) {
|
|
385
402
|
const previous = agent.switchToFallbackModel(lastTurn.errorMessage || lastTurn.text);
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export type BrowserControlAction = 'open' | 'snapshot' | 'click' | 'type' | 'eval' | 'back' | 'forward' | 'reload' | 'cdp' | 'use';
|
|
2
|
+
/** Renderer/host routing identity. Browser controls must never fall back to the
|
|
3
|
+
* currently focused conversation when a caller has a concrete target. */
|
|
4
|
+
export interface BrowserControlTarget {
|
|
5
|
+
workspaceId?: string;
|
|
6
|
+
conversationId?: string;
|
|
7
|
+
runtimeKey?: string;
|
|
8
|
+
}
|
|
2
9
|
export interface BrowserControlRequest {
|
|
3
10
|
action: BrowserControlAction;
|
|
11
|
+
target?: BrowserControlTarget;
|
|
4
12
|
url?: string;
|
|
5
13
|
selector?: string;
|
|
6
14
|
text?: string;
|
|
@@ -22,6 +22,8 @@ export interface BrowserUseHostPage {
|
|
|
22
22
|
}>;
|
|
23
23
|
evaluateFixed<T>(script: string, signal?: AbortSignal): Promise<T>;
|
|
24
24
|
clickAt(x: number, y: number, signal?: AbortSignal): Promise<void>;
|
|
25
|
+
/** Optional deterministic DOM click for embedded guests where native input can be dropped while the host tab is settling. */
|
|
26
|
+
clickElement?(token: string, signal?: AbortSignal): Promise<void>;
|
|
25
27
|
replaceFocusedText(text: string, signal?: AbortSignal): Promise<void>;
|
|
26
28
|
pressKey(key: string, signal?: AbortSignal): Promise<void>;
|
|
27
29
|
navigate(url: string, signal?: AbortSignal): Promise<void>;
|
|
@@ -46,6 +48,7 @@ export type BrowserUseHostPageResolver = (scope: BrowserUseScope) => Promise<Bro
|
|
|
46
48
|
export declare function browserUseObservationScript(maxChars: number, maxRefs: number): string;
|
|
47
49
|
export declare function browserUseProbeScript(token: string, scrollIntoView?: boolean): string;
|
|
48
50
|
export declare function browserUseFocusScript(token: string): string;
|
|
51
|
+
export declare function browserUseClickScript(token: string): string;
|
|
49
52
|
export declare function browserUseSelectScript(token: string, value: string): string;
|
|
50
53
|
export declare function browserUseScrollScript(token: string | undefined, deltaX: number, deltaY: number): string;
|
|
51
54
|
export declare function browserUseExtractScript(token: string | undefined, attribute: string | undefined, maxChars: number): string;
|
|
@@ -4,6 +4,7 @@ exports.NativeBrowserUsePageAdapter = void 0;
|
|
|
4
4
|
exports.browserUseObservationScript = browserUseObservationScript;
|
|
5
5
|
exports.browserUseProbeScript = browserUseProbeScript;
|
|
6
6
|
exports.browserUseFocusScript = browserUseFocusScript;
|
|
7
|
+
exports.browserUseClickScript = browserUseClickScript;
|
|
7
8
|
exports.browserUseSelectScript = browserUseSelectScript;
|
|
8
9
|
exports.browserUseScrollScript = browserUseScrollScript;
|
|
9
10
|
exports.browserUseExtractScript = browserUseExtractScript;
|
|
@@ -186,6 +187,17 @@ function browserUseProbeScript(token, scrollIntoView = false) {
|
|
|
186
187
|
function browserUseFocusScript(token) {
|
|
187
188
|
return `(() => { const el = document.querySelector(${json(token)}); if (!el) return false; if (typeof el.scrollIntoView === 'function') el.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'auto' }); el.focus({ preventScroll: true }); return document.activeElement === el || el.contains(document.activeElement); })()`;
|
|
188
189
|
}
|
|
190
|
+
function browserUseClickScript(token) {
|
|
191
|
+
return `(() => {
|
|
192
|
+
const el = document.querySelector(${json(token)});
|
|
193
|
+
if (!el) return { clicked: false, error: 'ref_not_found' };
|
|
194
|
+
if (typeof el.scrollIntoView === 'function') el.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'auto' });
|
|
195
|
+
if (typeof el.focus === 'function') el.focus({ preventScroll: true });
|
|
196
|
+
if (typeof el.click !== 'function') return { clicked: false, error: 'not_clickable' };
|
|
197
|
+
el.click();
|
|
198
|
+
return { clicked: true, tag: String(el.localName || 'element').toLowerCase() };
|
|
199
|
+
})()`;
|
|
200
|
+
}
|
|
189
201
|
function browserUseSelectScript(token, value) {
|
|
190
202
|
return `(() => {
|
|
191
203
|
const el = document.querySelector(${json(token)});
|
|
@@ -299,8 +311,13 @@ class NativeBrowserUsePageAdapter {
|
|
|
299
311
|
throw error;
|
|
300
312
|
}
|
|
301
313
|
if (request.action === 'click') {
|
|
302
|
-
|
|
303
|
-
|
|
314
|
+
if (page.clickElement) {
|
|
315
|
+
await page.clickElement(request.element.token, signal);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
const rect = probe.rect;
|
|
319
|
+
await page.clickAt(rect.x + rect.width / 2, rect.y + rect.height / 2, signal);
|
|
320
|
+
}
|
|
304
321
|
await page.waitForReady(signal);
|
|
305
322
|
return { clicked: true };
|
|
306
323
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-wide Computer-Use session state.
|
|
3
|
+
*
|
|
4
|
+
* The state is keyed by conversation runtime, not by Build/run id. A Build may finish
|
|
5
|
+
* or be interrupted/replaced while the conversation keeps its
|
|
6
|
+
* Computer-Use switch and lease. Only an explicit stop/toggle-off or runtime
|
|
7
|
+
* teardown releases it.
|
|
8
|
+
*/
|
|
9
|
+
export interface ComputerUseSessionScope {
|
|
10
|
+
runtimeKey: string;
|
|
11
|
+
ownerLabel: string;
|
|
12
|
+
workspacePath?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ComputerUseSessionState {
|
|
15
|
+
runtimeKey: string;
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
occupied: boolean;
|
|
18
|
+
ownerLabel?: string;
|
|
19
|
+
updatedAt?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const COMPUTER_USE_OCCUPIED_MARKER = "computerUse occupied";
|
|
22
|
+
export declare const COMPUTER_USE_LOCK_TTL_MS: number;
|
|
23
|
+
export declare class ComputerUseSessionRegistry {
|
|
24
|
+
private readonly ttlMs;
|
|
25
|
+
private readonly enabledByRuntime;
|
|
26
|
+
private activeLease;
|
|
27
|
+
constructor(ttlMs?: number);
|
|
28
|
+
authorize(action: string, scope: ComputerUseSessionScope, dryRun?: boolean): string | null;
|
|
29
|
+
complete(action: string, scope: ComputerUseSessionScope): void;
|
|
30
|
+
setEnabled(scope: ComputerUseSessionScope, enabled: boolean): {
|
|
31
|
+
ok: true;
|
|
32
|
+
state: ComputerUseSessionState;
|
|
33
|
+
} | {
|
|
34
|
+
ok: false;
|
|
35
|
+
error: string;
|
|
36
|
+
state: ComputerUseSessionState;
|
|
37
|
+
};
|
|
38
|
+
state(runtimeKey: string): ComputerUseSessionState;
|
|
39
|
+
cancelTarget(runtimeKey: string): boolean;
|
|
40
|
+
private clearExpired;
|
|
41
|
+
private occupiedError;
|
|
42
|
+
}
|
|
43
|
+
export declare const defaultComputerUseSessionRegistry: ComputerUseSessionRegistry;
|
|
44
|
+
//# sourceMappingURL=computerUseSession.d.ts.map
|