newmark-agent 0.5.10 → 0.5.12
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/conversation-utility-host.bundle.cjs +94 -27
- package/dist/core/agent.d.ts +1 -1
- package/dist/core/agent.js +24 -5
- package/dist/core/conversationKernel.d.ts +4 -0
- package/dist/core/conversationKernel.js +65 -3
- package/dist/core/electronUtilityAgentClient.js +21 -0
- package/dist/core/runtimeLifecycle.d.ts +2 -0
- package/dist/core/runtimeLifecycle.js +57 -8
- package/dist/main.js +6 -6
- package/dist/ui/index.html +128 -9
- package/dist/wsl-agent-host.bundle.cjs +94 -27
- package/package.json +2 -1
|
@@ -8383,7 +8383,7 @@ var require_createWorker = __commonJS({
|
|
|
8383
8383
|
...defaultOptions,
|
|
8384
8384
|
..._options
|
|
8385
8385
|
});
|
|
8386
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
8512
|
-
delete
|
|
8511
|
+
promises3[promiseId].resolve({ jobId, data });
|
|
8512
|
+
delete promises3[promiseId];
|
|
8513
8513
|
} else if (status === "reject") {
|
|
8514
|
-
|
|
8515
|
-
delete
|
|
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
|
|
54009
|
+
const promises3 = [this[kAgent].close()];
|
|
54010
54010
|
if (this[kClient]) {
|
|
54011
|
-
|
|
54011
|
+
promises3.push(this[kClient].close());
|
|
54012
54012
|
}
|
|
54013
|
-
return Promise.all(
|
|
54013
|
+
return Promise.all(promises3);
|
|
54014
54014
|
}
|
|
54015
54015
|
[kDestroy]() {
|
|
54016
|
-
const
|
|
54016
|
+
const promises3 = [this[kAgent].destroy()];
|
|
54017
54017
|
if (this[kClient]) {
|
|
54018
|
-
|
|
54018
|
+
promises3.push(this[kClient].destroy());
|
|
54019
54019
|
}
|
|
54020
|
-
return Promise.all(
|
|
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
|
-
|
|
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
|
|
|
@@ -348522,12 +348524,15 @@ Conversation title (a few words):`;
|
|
|
348522
348524
|
defaultInputMode() {
|
|
348523
348525
|
return this.config.getStr("general", "default_input") === "next" ? "next" : "guide";
|
|
348524
348526
|
}
|
|
348525
|
-
abortActiveKernelRun() {
|
|
348527
|
+
abortActiveKernelRun(reason = "unspecified") {
|
|
348526
348528
|
let aborted = false;
|
|
348527
348529
|
this.subagents.pauseScheduling();
|
|
348528
348530
|
if (this.activeProcessAbortController && !this.activeProcessAbortController.signal.aborted) {
|
|
348529
348531
|
const abortError5 = new Error("Agent run aborted");
|
|
348530
348532
|
abortError5.name = "AbortError";
|
|
348533
|
+
if (process.env.NEWMARK_PROVIDER_DIAGNOSTICS === "1") {
|
|
348534
|
+
console.error(`[NewmarkKernel] abort-request reason=${String(reason).slice(0, 160)} conversation=${this.activeConversationId}`);
|
|
348535
|
+
}
|
|
348531
348536
|
this.activeProcessAbortController.abort(abortError5);
|
|
348532
348537
|
aborted = true;
|
|
348533
348538
|
}
|
|
@@ -348536,7 +348541,7 @@ Conversation title (a few words):`;
|
|
|
348536
348541
|
aborted = true;
|
|
348537
348542
|
}
|
|
348538
348543
|
for (const peer of this.activePeerAgents.values()) {
|
|
348539
|
-
aborted = peer.abortActiveKernelRun() || aborted;
|
|
348544
|
+
aborted = peer.abortActiveKernelRun(reason) || aborted;
|
|
348540
348545
|
}
|
|
348541
348546
|
this.pendingAgentKernelQueue = [];
|
|
348542
348547
|
return aborted;
|
|
@@ -350043,10 +350048,16 @@ ${summary}`, segment, "local-summarize", true);
|
|
|
350043
350048
|
return { compressed: true, rounds, segments, droppedMessages, estimatedTokens: this.estimateContextTokens(candidate), maxTokens };
|
|
350044
350049
|
}
|
|
350045
350050
|
compactHistoricalImages(messages) {
|
|
350051
|
+
const continuationIndex = messages.reduce((found, message, index) => {
|
|
350052
|
+
const content = String(message?.content || "");
|
|
350053
|
+
return content.includes("[Post-Compression Task Continuation]") ? index : found;
|
|
350054
|
+
}, -1);
|
|
350046
350055
|
const lastParts = Array.isArray(messages.at(-1)?.content) ? messages.at(-1).content : [];
|
|
350047
|
-
const newestImageMessage = lastParts.some((part) => part?.type === "image_url") ? messages.length - 1 : -1;
|
|
350056
|
+
const newestImageMessage = continuationIndex < 0 && lastParts.some((part) => part?.type === "image_url") ? messages.length - 1 : -1;
|
|
350048
350057
|
return messages.map((message, index) => {
|
|
350049
|
-
if (!Array.isArray(message.content)
|
|
350058
|
+
if (!Array.isArray(message.content)) return { ...message };
|
|
350059
|
+
const preserveRecentImage = continuationIndex >= 0 && index > continuationIndex;
|
|
350060
|
+
if (preserveRecentImage || index === newestImageMessage) return { ...message };
|
|
350050
350061
|
const parts = message.content.flatMap((part) => {
|
|
350051
350062
|
if (part?.type !== "image_url") return [{ ...part }];
|
|
350052
350063
|
return [{ type: "text", text: "[Historical image attachment omitted after context compression.]" }];
|
|
@@ -352970,12 +352981,16 @@ ${text.slice(-tailChars).trimStart()}`;
|
|
|
352970
352981
|
}
|
|
352971
352982
|
buildSystemPrompt() {
|
|
352972
352983
|
const cwd = this.workspace.current?.path || this.rootPath;
|
|
352984
|
+
const newmarkConfigRoot = path28.resolve(this.rootPath);
|
|
352985
|
+
const newmarkConfigFile = path28.join(newmarkConfigRoot, "config.json");
|
|
352973
352986
|
const enabledSkills = this.skills.active();
|
|
352974
352987
|
const globalPromptPath = path28.join(this.rootPath, "agent.md");
|
|
352975
352988
|
const globalPrompt = normalizeInjectedPrompt(fs25.existsSync(globalPromptPath) ? fs25.readFileSync(globalPromptPath, "utf-8") : "");
|
|
352976
352989
|
const workspacePrompt = normalizeInjectedPrompt(this.workspace.currentAgentPrompt());
|
|
352977
352990
|
const identity = JSON.stringify({
|
|
352978
352991
|
cwd,
|
|
352992
|
+
newmarkConfigRoot,
|
|
352993
|
+
newmarkConfigFile,
|
|
352979
352994
|
mode: this.mode,
|
|
352980
352995
|
conversationId: this.activeConversationId,
|
|
352981
352996
|
subagent: this.isSubagentRuntime ? [this.subagentName, this.subagentPrompt] : null,
|
|
@@ -352994,6 +353009,9 @@ ${text.slice(-tailChars).trimStart()}`;
|
|
|
352994
353009
|
if (this.systemPromptCache?.identity === identity) return this.systemPromptCache.value;
|
|
352995
353010
|
const parts = [`${CORE_SYSTEM_PROMPT}
|
|
352996
353011
|
|
|
353012
|
+
## Newmark Runtime Configuration
|
|
353013
|
+
Newmark's own user-level configuration and runtime state are stored by default under ${newmarkConfigRoot} (the conventional user path is user/.Newmark). The primary configuration file is ${newmarkConfigFile}; provider credentials, conversation state, caches, Work/Flow data, Memory Lab, skills, and archives may also live beneath this root. Treat these files as Newmark internal state: read or modify them only when the user explicitly asks, never expose API keys or tokens, and do not confuse this runtime root with the active workspace.
|
|
353014
|
+
|
|
352997
353015
|
## Current Working Directory
|
|
352998
353016
|
${cwd}
|
|
352999
353017
|
|
|
@@ -353739,7 +353757,12 @@ var ConversationKernel = class {
|
|
|
353739
353757
|
...images?.length ? { images } : {},
|
|
353740
353758
|
...attachments?.length ? { attachments } : {},
|
|
353741
353759
|
...visible ? { visibleUserInput: visible } : {},
|
|
353742
|
-
...visibleMode ? { visibleMode } : {}
|
|
353760
|
+
...visibleMode ? { visibleMode } : {},
|
|
353761
|
+
// A normal user follow-up must never inherit the identity metadata that
|
|
353762
|
+
// was only needed while it lived in the runtime queue. In particular,
|
|
353763
|
+
// do not let a stale/forwarded hiddenUserInput flag classify it as an
|
|
353764
|
+
// Agent-generated continuation when Agent.process persists the turn.
|
|
353765
|
+
hiddenUserInput: message.hiddenUserInput === true && !message.clientMessageId
|
|
353743
353766
|
};
|
|
353744
353767
|
}
|
|
353745
353768
|
queueItems(target) {
|
|
@@ -354312,7 +354335,7 @@ ${item.goalObjective}` : item.text,
|
|
|
354312
354335
|
this.deferOutstandingGuides(runtime);
|
|
354313
354336
|
const checkpointed = this.checkpoint(runtime.target).checkpointed;
|
|
354314
354337
|
runtime.stopCheckpointed = checkpointed;
|
|
354315
|
-
runtime.runner.abortActiveKernelRun();
|
|
354338
|
+
runtime.runner.abortActiveKernelRun("user_stop");
|
|
354316
354339
|
runtime.runner.emitWorkEvent({
|
|
354317
354340
|
type: "status",
|
|
354318
354341
|
content: "Stop requested. Saving progress and interrupting this conversation.",
|
|
@@ -354446,6 +354469,7 @@ ${item.goalObjective}` : item.text,
|
|
|
354446
354469
|
async run(runtime, message, options) {
|
|
354447
354470
|
this.applyOptions(runtime.runner, options);
|
|
354448
354471
|
let lastTokens = await this.runSingle(runtime, message);
|
|
354472
|
+
this.rememberAutomaticAssistantFingerprint(runtime, message);
|
|
354449
354473
|
if (runtime.stopRequestedRunId === runtime.runId) {
|
|
354450
354474
|
this.mirrorHostIfTargetActive(runtime);
|
|
354451
354475
|
return this.result(runtime, lastTokens);
|
|
@@ -354472,6 +354496,7 @@ ${item.goalObjective}` : item.text,
|
|
|
354472
354496
|
}
|
|
354473
354497
|
if (batchGuides.length === 1) {
|
|
354474
354498
|
lastTokens = await this.runSingle(runtime, next.message, next.queueMode);
|
|
354499
|
+
if (this.repeatedAutomaticAssistant(runtime, next.message, next.queueMode)) break;
|
|
354475
354500
|
continue;
|
|
354476
354501
|
}
|
|
354477
354502
|
const batchText = batchGuides.map((guide, index) => `Guide ${index + 1}: ${guide.text}`).join("\n");
|
|
@@ -354482,8 +354507,11 @@ ${batchText}`,
|
|
|
354482
354507
|
batchGuides
|
|
354483
354508
|
};
|
|
354484
354509
|
lastTokens = await this.runSingle(runtime, batchMessage, "steer");
|
|
354510
|
+
if (this.repeatedAutomaticAssistant(runtime, batchMessage, "steer")) break;
|
|
354485
354511
|
} else {
|
|
354486
|
-
|
|
354512
|
+
const drained = this.drainQueuedFollowUpMessage(next.message);
|
|
354513
|
+
lastTokens = await this.runSingle(runtime, drained, next.queueMode);
|
|
354514
|
+
if (this.repeatedAutomaticAssistant(runtime, drained, next.queueMode)) break;
|
|
354487
354515
|
}
|
|
354488
354516
|
}
|
|
354489
354517
|
const rootMessage = runtime.runner.subagents.readRootInbox()[0];
|
|
@@ -354518,6 +354546,35 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354518
354546
|
this.mirrorHostIfTargetActive(runtime);
|
|
354519
354547
|
return this.result(runtime, lastTokens);
|
|
354520
354548
|
}
|
|
354549
|
+
automaticMessage(message, queueMode) {
|
|
354550
|
+
return queueMode === "steer" || typeof message !== "string" && (message.hiddenUserInput === true || message.goalContinuation === true || !!message.batchGuides?.length);
|
|
354551
|
+
}
|
|
354552
|
+
currentAssistantFingerprint(runtime) {
|
|
354553
|
+
const messages = runtime.runner.chatMessages || [];
|
|
354554
|
+
const last = messages[messages.length - 1];
|
|
354555
|
+
if (!last || last.role !== "assistant") return "";
|
|
354556
|
+
return String(last.content || "").trim().replace(/\s+/g, " ").toLowerCase().slice(0, 4e3);
|
|
354557
|
+
}
|
|
354558
|
+
rememberAutomaticAssistantFingerprint(runtime, message, queueMode) {
|
|
354559
|
+
if (this.automaticMessage(message, queueMode)) runtime.lastAutomaticAssistantFingerprint = this.currentAssistantFingerprint(runtime) || void 0;
|
|
354560
|
+
}
|
|
354561
|
+
repeatedAutomaticAssistant(runtime, message, queueMode) {
|
|
354562
|
+
if (!this.automaticMessage(message, queueMode)) return false;
|
|
354563
|
+
const fingerprint2 = this.currentAssistantFingerprint(runtime);
|
|
354564
|
+
if (!fingerprint2) return false;
|
|
354565
|
+
if (runtime.lastAutomaticAssistantFingerprint === fingerprint2) {
|
|
354566
|
+
runtime.pendingNextTurn = runtime.pendingNextTurn.filter((item) => {
|
|
354567
|
+
const automatic = item.queueMode === "steer" || typeof item.message !== "string" && item.message.hiddenUserInput === true;
|
|
354568
|
+
if (automatic) runtime.runner.consumeConversationContinuation({ content: typeof item.message === "string" ? item.message : item.message.text, queueMode: item.queueMode, clientMessageId: typeof item.message === "string" ? void 0 : item.message.clientMessageId });
|
|
354569
|
+
return !automatic;
|
|
354570
|
+
});
|
|
354571
|
+
runtime.runner.recordWorkStatus("Automatic continuation stopped after a repeated assistant response.");
|
|
354572
|
+
this.emitQueueUpdate(runtime);
|
|
354573
|
+
return true;
|
|
354574
|
+
}
|
|
354575
|
+
runtime.lastAutomaticAssistantFingerprint = fingerprint2;
|
|
354576
|
+
return false;
|
|
354577
|
+
}
|
|
354521
354578
|
/**
|
|
354522
354579
|
* Apply a model selection recorded while a Build block was running. The
|
|
354523
354580
|
* in-flight block never switches mid-block; the switch takes effect the next
|
|
@@ -354557,7 +354614,16 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354557
354614
|
tokens = await Promise.race([
|
|
354558
354615
|
runtime.runner.process(message),
|
|
354559
354616
|
new Promise((_3, reject) => {
|
|
354560
|
-
timeout = setTimeout(() =>
|
|
354617
|
+
timeout = setTimeout(() => {
|
|
354618
|
+
runtime.runner.abortActiveKernelRun(`process_timeout_${timeoutMs}ms`);
|
|
354619
|
+
runtime.runner.emitWorkEvent({
|
|
354620
|
+
type: "error",
|
|
354621
|
+
content: `Process timeout (${Math.round(timeoutMs / 1e3)}s); the run was aborted to prevent a stale worker from continuing.`,
|
|
354622
|
+
status: "error",
|
|
354623
|
+
runId: runtime.runId
|
|
354624
|
+
});
|
|
354625
|
+
reject(new Error(`Process timeout (${Math.round(timeoutMs / 1e3)}s); run aborted and checkpointed`));
|
|
354626
|
+
}, timeoutMs);
|
|
354561
354627
|
})
|
|
354562
354628
|
]);
|
|
354563
354629
|
} catch (error) {
|
|
@@ -354618,7 +354684,8 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354618
354684
|
guideReceipts: /* @__PURE__ */ new Map(),
|
|
354619
354685
|
guideEnvelopes: /* @__PURE__ */ new Map(),
|
|
354620
354686
|
goalContinuationTimer: void 0,
|
|
354621
|
-
pendingContinuationRunId: void 0
|
|
354687
|
+
pendingContinuationRunId: void 0,
|
|
354688
|
+
lastAutomaticAssistantFingerprint: void 0
|
|
354622
354689
|
};
|
|
354623
354690
|
runner.setGoalContinuationGate(() => {
|
|
354624
354691
|
this.queueState(runtime);
|
package/dist/core/agent.d.ts
CHANGED
|
@@ -731,7 +731,7 @@ export declare class Agent {
|
|
|
731
731
|
persistActiveConversationSelection(id: string, ws?: WorkspaceInfo | null): string;
|
|
732
732
|
setInputMode(mode: string): InputMode;
|
|
733
733
|
private defaultInputMode;
|
|
734
|
-
abortActiveKernelRun(): boolean;
|
|
734
|
+
abortActiveKernelRun(reason?: string): boolean;
|
|
735
735
|
activeProcessSignal(): AbortSignal | undefined;
|
|
736
736
|
recordWorkRunPrimaryPrompt(content: string): void;
|
|
737
737
|
conversationBuildHistory(limit?: number): Array<{
|
package/dist/core/agent.js
CHANGED
|
@@ -4119,12 +4119,15 @@ class Agent {
|
|
|
4119
4119
|
defaultInputMode() {
|
|
4120
4120
|
return this.config.getStr('general', 'default_input') === 'next' ? 'next' : 'guide';
|
|
4121
4121
|
}
|
|
4122
|
-
abortActiveKernelRun() {
|
|
4122
|
+
abortActiveKernelRun(reason = 'unspecified') {
|
|
4123
4123
|
let aborted = false;
|
|
4124
4124
|
this.subagents.pauseScheduling();
|
|
4125
4125
|
if (this.activeProcessAbortController && !this.activeProcessAbortController.signal.aborted) {
|
|
4126
4126
|
const abortError = new Error('Agent run aborted');
|
|
4127
4127
|
abortError.name = 'AbortError';
|
|
4128
|
+
if (process.env.NEWMARK_PROVIDER_DIAGNOSTICS === '1') {
|
|
4129
|
+
console.error(`[NewmarkKernel] abort-request reason=${String(reason).slice(0, 160)} conversation=${this.activeConversationId}`);
|
|
4130
|
+
}
|
|
4128
4131
|
this.activeProcessAbortController.abort(abortError);
|
|
4129
4132
|
aborted = true;
|
|
4130
4133
|
}
|
|
@@ -4133,7 +4136,7 @@ class Agent {
|
|
|
4133
4136
|
aborted = true;
|
|
4134
4137
|
}
|
|
4135
4138
|
for (const peer of this.activePeerAgents.values()) {
|
|
4136
|
-
aborted = peer.abortActiveKernelRun() || aborted;
|
|
4139
|
+
aborted = peer.abortActiveKernelRun(reason) || aborted;
|
|
4137
4140
|
}
|
|
4138
4141
|
this.pendingAgentKernelQueue = [];
|
|
4139
4142
|
return aborted;
|
|
@@ -5780,10 +5783,22 @@ class Agent {
|
|
|
5780
5783
|
return { compressed: true, rounds, segments, droppedMessages, estimatedTokens: this.estimateContextTokens(candidate), maxTokens };
|
|
5781
5784
|
}
|
|
5782
5785
|
compactHistoricalImages(messages) {
|
|
5786
|
+
// The post-compression continuation anchor separates summarized history
|
|
5787
|
+
// from the retained recent window. Preserve every user image in that
|
|
5788
|
+
// recent window (not only the last image message): a burst of consecutive
|
|
5789
|
+
// uploads must remain available to the next vision request. Images older
|
|
5790
|
+
// than the retained window are still replaced by bounded text markers.
|
|
5791
|
+
const continuationIndex = messages.reduce((found, message, index) => {
|
|
5792
|
+
const content = String(message?.content || '');
|
|
5793
|
+
return content.includes('[Post-Compression Task Continuation]') ? index : found;
|
|
5794
|
+
}, -1);
|
|
5783
5795
|
const lastParts = Array.isArray(messages.at(-1)?.content) ? messages.at(-1).content : [];
|
|
5784
|
-
const newestImageMessage = lastParts.some(part => part?.type === 'image_url') ? messages.length - 1 : -1;
|
|
5796
|
+
const newestImageMessage = continuationIndex < 0 && lastParts.some(part => part?.type === 'image_url') ? messages.length - 1 : -1;
|
|
5785
5797
|
return messages.map((message, index) => {
|
|
5786
|
-
if (!Array.isArray(message.content)
|
|
5798
|
+
if (!Array.isArray(message.content))
|
|
5799
|
+
return { ...message };
|
|
5800
|
+
const preserveRecentImage = continuationIndex >= 0 && index > continuationIndex;
|
|
5801
|
+
if (preserveRecentImage || index === newestImageMessage)
|
|
5787
5802
|
return { ...message };
|
|
5788
5803
|
const parts = message.content.flatMap(part => {
|
|
5789
5804
|
if (part?.type !== 'image_url')
|
|
@@ -9125,12 +9140,16 @@ class Agent {
|
|
|
9125
9140
|
}
|
|
9126
9141
|
buildSystemPrompt() {
|
|
9127
9142
|
const cwd = this.workspace.current?.path || this.rootPath;
|
|
9143
|
+
const newmarkConfigRoot = path.resolve(this.rootPath);
|
|
9144
|
+
const newmarkConfigFile = path.join(newmarkConfigRoot, 'config.json');
|
|
9128
9145
|
const enabledSkills = this.skills.active();
|
|
9129
9146
|
const globalPromptPath = path.join(this.rootPath, 'agent.md');
|
|
9130
9147
|
const globalPrompt = normalizeInjectedPrompt(fs.existsSync(globalPromptPath) ? fs.readFileSync(globalPromptPath, 'utf-8') : '');
|
|
9131
9148
|
const workspacePrompt = normalizeInjectedPrompt(this.workspace.currentAgentPrompt());
|
|
9132
9149
|
const identity = JSON.stringify({
|
|
9133
9150
|
cwd,
|
|
9151
|
+
newmarkConfigRoot,
|
|
9152
|
+
newmarkConfigFile,
|
|
9134
9153
|
mode: this.mode,
|
|
9135
9154
|
conversationId: this.activeConversationId,
|
|
9136
9155
|
subagent: this.isSubagentRuntime ? [this.subagentName, this.subagentPrompt] : null,
|
|
@@ -9148,7 +9167,7 @@ class Agent {
|
|
|
9148
9167
|
});
|
|
9149
9168
|
if (this.systemPromptCache?.identity === identity)
|
|
9150
9169
|
return this.systemPromptCache.value;
|
|
9151
|
-
const parts = [`${CORE_SYSTEM_PROMPT}\n\n## Current Working Directory\n${cwd}\n\nWhen using file tools (read, write, edit, glob), use ABSOLUTE paths rooted at this directory. Never guess paths. First use \`pwd\` or \`bash\` to verify.`];
|
|
9170
|
+
const parts = [`${CORE_SYSTEM_PROMPT}\n\n## Newmark Runtime Configuration\nNewmark's own user-level configuration and runtime state are stored by default under ${newmarkConfigRoot} (the conventional user path is user/.Newmark). The primary configuration file is ${newmarkConfigFile}; provider credentials, conversation state, caches, Work/Flow data, Memory Lab, skills, and archives may also live beneath this root. Treat these files as Newmark internal state: read or modify them only when the user explicitly asks, never expose API keys or tokens, and do not confuse this runtime root with the active workspace.\n\n## Current Working Directory\n${cwd}\n\nWhen using file tools (read, write, edit, glob), use ABSOLUTE paths rooted at this directory. Never guess paths. First use \`pwd\` or \`bash\` to verify.`];
|
|
9152
9171
|
if (this.isSubagentRuntime) {
|
|
9153
9172
|
parts.push([
|
|
9154
9173
|
'## Subagent Sandbox',
|
|
@@ -285,6 +285,10 @@ export declare class ConversationKernel {
|
|
|
285
285
|
private settleCooperativeStop;
|
|
286
286
|
private stopAutomaticContinuationAfterError;
|
|
287
287
|
private run;
|
|
288
|
+
private automaticMessage;
|
|
289
|
+
private currentAssistantFingerprint;
|
|
290
|
+
private rememberAutomaticAssistantFingerprint;
|
|
291
|
+
private repeatedAutomaticAssistant;
|
|
288
292
|
/**
|
|
289
293
|
* Apply a model selection recorded while a Build block was running. The
|
|
290
294
|
* in-flight block never switches mid-block; the switch takes effect the next
|
|
@@ -85,6 +85,11 @@ class ConversationKernel {
|
|
|
85
85
|
...(attachments?.length ? { attachments } : {}),
|
|
86
86
|
...(visible ? { visibleUserInput: visible } : {}),
|
|
87
87
|
...(visibleMode ? { visibleMode } : {}),
|
|
88
|
+
// A normal user follow-up must never inherit the identity metadata that
|
|
89
|
+
// was only needed while it lived in the runtime queue. In particular,
|
|
90
|
+
// do not let a stale/forwarded hiddenUserInput flag classify it as an
|
|
91
|
+
// Agent-generated continuation when Agent.process persists the turn.
|
|
92
|
+
hiddenUserInput: message.hiddenUserInput === true && !message.clientMessageId,
|
|
88
93
|
};
|
|
89
94
|
}
|
|
90
95
|
queueItems(target) {
|
|
@@ -718,7 +723,7 @@ class ConversationKernel {
|
|
|
718
723
|
this.deferOutstandingGuides(runtime);
|
|
719
724
|
const checkpointed = this.checkpoint(runtime.target).checkpointed;
|
|
720
725
|
runtime.stopCheckpointed = checkpointed;
|
|
721
|
-
runtime.runner.abortActiveKernelRun();
|
|
726
|
+
runtime.runner.abortActiveKernelRun('user_stop');
|
|
722
727
|
runtime.runner.emitWorkEvent({
|
|
723
728
|
type: 'status',
|
|
724
729
|
content: 'Stop requested. Saving progress and interrupting this conversation.',
|
|
@@ -874,6 +879,7 @@ class ConversationKernel {
|
|
|
874
879
|
async run(runtime, message, options) {
|
|
875
880
|
this.applyOptions(runtime.runner, options);
|
|
876
881
|
let lastTokens = await this.runSingle(runtime, message);
|
|
882
|
+
this.rememberAutomaticAssistantFingerprint(runtime, message);
|
|
877
883
|
if (runtime.stopRequestedRunId === runtime.runId) {
|
|
878
884
|
this.mirrorHostIfTargetActive(runtime);
|
|
879
885
|
return this.result(runtime, lastTokens);
|
|
@@ -904,6 +910,8 @@ class ConversationKernel {
|
|
|
904
910
|
}
|
|
905
911
|
if (batchGuides.length === 1) {
|
|
906
912
|
lastTokens = await this.runSingle(runtime, next.message, next.queueMode);
|
|
913
|
+
if (this.repeatedAutomaticAssistant(runtime, next.message, next.queueMode))
|
|
914
|
+
break;
|
|
907
915
|
continue;
|
|
908
916
|
}
|
|
909
917
|
const batchText = batchGuides.map((guide, index) => `Guide ${index + 1}: ${guide.text}`).join('\n');
|
|
@@ -913,9 +921,14 @@ class ConversationKernel {
|
|
|
913
921
|
batchGuides,
|
|
914
922
|
};
|
|
915
923
|
lastTokens = await this.runSingle(runtime, batchMessage, 'steer');
|
|
924
|
+
if (this.repeatedAutomaticAssistant(runtime, batchMessage, 'steer'))
|
|
925
|
+
break;
|
|
916
926
|
}
|
|
917
927
|
else {
|
|
918
|
-
|
|
928
|
+
const drained = this.drainQueuedFollowUpMessage(next.message);
|
|
929
|
+
lastTokens = await this.runSingle(runtime, drained, next.queueMode);
|
|
930
|
+
if (this.repeatedAutomaticAssistant(runtime, drained, next.queueMode))
|
|
931
|
+
break;
|
|
919
932
|
}
|
|
920
933
|
}
|
|
921
934
|
const rootMessage = runtime.runner.subagents.readRootInbox()[0];
|
|
@@ -956,6 +969,41 @@ class ConversationKernel {
|
|
|
956
969
|
this.mirrorHostIfTargetActive(runtime);
|
|
957
970
|
return this.result(runtime, lastTokens);
|
|
958
971
|
}
|
|
972
|
+
automaticMessage(message, queueMode) {
|
|
973
|
+
return queueMode === 'steer'
|
|
974
|
+
|| (typeof message !== 'string' && (message.hiddenUserInput === true || message.goalContinuation === true || !!message.batchGuides?.length));
|
|
975
|
+
}
|
|
976
|
+
currentAssistantFingerprint(runtime) {
|
|
977
|
+
const messages = runtime.runner.chatMessages || [];
|
|
978
|
+
const last = messages[messages.length - 1];
|
|
979
|
+
if (!last || last.role !== 'assistant')
|
|
980
|
+
return '';
|
|
981
|
+
return String(last.content || '').trim().replace(/\s+/g, ' ').toLowerCase().slice(0, 4000);
|
|
982
|
+
}
|
|
983
|
+
rememberAutomaticAssistantFingerprint(runtime, message, queueMode) {
|
|
984
|
+
if (this.automaticMessage(message, queueMode))
|
|
985
|
+
runtime.lastAutomaticAssistantFingerprint = this.currentAssistantFingerprint(runtime) || undefined;
|
|
986
|
+
}
|
|
987
|
+
repeatedAutomaticAssistant(runtime, message, queueMode) {
|
|
988
|
+
if (!this.automaticMessage(message, queueMode))
|
|
989
|
+
return false;
|
|
990
|
+
const fingerprint = this.currentAssistantFingerprint(runtime);
|
|
991
|
+
if (!fingerprint)
|
|
992
|
+
return false;
|
|
993
|
+
if (runtime.lastAutomaticAssistantFingerprint === fingerprint) {
|
|
994
|
+
runtime.pendingNextTurn = runtime.pendingNextTurn.filter(item => {
|
|
995
|
+
const automatic = item.queueMode === 'steer' || (typeof item.message !== 'string' && item.message.hiddenUserInput === true);
|
|
996
|
+
if (automatic)
|
|
997
|
+
runtime.runner.consumeConversationContinuation({ content: typeof item.message === 'string' ? item.message : item.message.text, queueMode: item.queueMode, clientMessageId: typeof item.message === 'string' ? undefined : item.message.clientMessageId });
|
|
998
|
+
return !automatic;
|
|
999
|
+
});
|
|
1000
|
+
runtime.runner.recordWorkStatus('Automatic continuation stopped after a repeated assistant response.');
|
|
1001
|
+
this.emitQueueUpdate(runtime);
|
|
1002
|
+
return true;
|
|
1003
|
+
}
|
|
1004
|
+
runtime.lastAutomaticAssistantFingerprint = fingerprint;
|
|
1005
|
+
return false;
|
|
1006
|
+
}
|
|
959
1007
|
/**
|
|
960
1008
|
* Apply a model selection recorded while a Build block was running. The
|
|
961
1009
|
* in-flight block never switches mid-block; the switch takes effect the next
|
|
@@ -999,7 +1047,20 @@ class ConversationKernel {
|
|
|
999
1047
|
tokens = await Promise.race([
|
|
1000
1048
|
runtime.runner.process(message),
|
|
1001
1049
|
new Promise((_, reject) => {
|
|
1002
|
-
timeout = setTimeout(() =>
|
|
1050
|
+
timeout = setTimeout(() => {
|
|
1051
|
+
// A timeout must cancel the provider/kernel operation as well as
|
|
1052
|
+
// reject the caller. Previously only the Promise.race rejected,
|
|
1053
|
+
// leaving the worker running in the background and making the
|
|
1054
|
+
// UI observe a later unexplained interruption.
|
|
1055
|
+
runtime.runner.abortActiveKernelRun(`process_timeout_${timeoutMs}ms`);
|
|
1056
|
+
runtime.runner.emitWorkEvent({
|
|
1057
|
+
type: 'error',
|
|
1058
|
+
content: `Process timeout (${Math.round(timeoutMs / 1000)}s); the run was aborted to prevent a stale worker from continuing.`,
|
|
1059
|
+
status: 'error',
|
|
1060
|
+
runId: runtime.runId,
|
|
1061
|
+
});
|
|
1062
|
+
reject(new Error(`Process timeout (${Math.round(timeoutMs / 1000)}s); run aborted and checkpointed`));
|
|
1063
|
+
}, timeoutMs);
|
|
1003
1064
|
}),
|
|
1004
1065
|
]);
|
|
1005
1066
|
}
|
|
@@ -1067,6 +1128,7 @@ class ConversationKernel {
|
|
|
1067
1128
|
guideEnvelopes: new Map(),
|
|
1068
1129
|
goalContinuationTimer: undefined,
|
|
1069
1130
|
pendingContinuationRunId: undefined,
|
|
1131
|
+
lastAutomaticAssistantFingerprint: undefined,
|
|
1070
1132
|
};
|
|
1071
1133
|
runner.setGoalContinuationGate(() => {
|
|
1072
1134
|
this.queueState(runtime);
|
|
@@ -1378,6 +1378,27 @@ class ElectronUtilityAgentClient {
|
|
|
1378
1378
|
if (this.child !== child)
|
|
1379
1379
|
return;
|
|
1380
1380
|
const error = new Error(`Electron utility runtime exited (${code}): ${this.lastError || 'no stderr'}`);
|
|
1381
|
+
// Surface unexpected worker death as a target-scoped terminal error. A
|
|
1382
|
+
// silent child exit used to leave the renderer with a generic interrupted
|
|
1383
|
+
// state and no explanation of whether the provider, runtime, or process
|
|
1384
|
+
// supervisor was responsible.
|
|
1385
|
+
if (code !== 0 && !this.restartQuarantine) {
|
|
1386
|
+
const event = {
|
|
1387
|
+
id: `utility-runtime-exit-${process.pid}-${Date.now()}`,
|
|
1388
|
+
conversationId: this.target.conversationId,
|
|
1389
|
+
type: 'error',
|
|
1390
|
+
content: error.message,
|
|
1391
|
+
mode: 'build',
|
|
1392
|
+
model: '',
|
|
1393
|
+
timestamp: new Date().toISOString(),
|
|
1394
|
+
workspaceId: this.target.workspaceId,
|
|
1395
|
+
workspaceKey: this.target.workspaceKey,
|
|
1396
|
+
runtimeKey: this.target.runtimeKey,
|
|
1397
|
+
status: 'error',
|
|
1398
|
+
};
|
|
1399
|
+
for (const listener of this.listeners)
|
|
1400
|
+
listener(event);
|
|
1401
|
+
}
|
|
1381
1402
|
this.detachChild(child, error);
|
|
1382
1403
|
}
|
|
1383
1404
|
detachChild(child, error) {
|
|
@@ -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
|
-
|
|
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
|
-
|
|
140
|
-
|
|
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
|
-
//
|
|
1807
|
-
// startup
|
|
1808
|
-
|
|
1809
|
-
|
|
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';
|
package/dist/ui/index.html
CHANGED
|
@@ -1545,6 +1545,7 @@ button.left-ws-item {
|
|
|
1545
1545
|
font-size: 12px;
|
|
1546
1546
|
}
|
|
1547
1547
|
.md-rendered pre {
|
|
1548
|
+
position: relative;
|
|
1548
1549
|
margin: 8px 0;
|
|
1549
1550
|
padding: 10px 12px;
|
|
1550
1551
|
overflow-x: auto;
|
|
@@ -2138,6 +2139,19 @@ button.left-ws-item {
|
|
|
2138
2139
|
/* Opaque theme-tinted foundation below the transcript, rails and glass. */
|
|
2139
2140
|
background-color: var(--chat-canvas);
|
|
2140
2141
|
}
|
|
2142
|
+
.md-code-copy {
|
|
2143
|
+
position: absolute;
|
|
2144
|
+
top: 6px;
|
|
2145
|
+
right: 7px;
|
|
2146
|
+
border: 1px solid var(--glass-border-1);
|
|
2147
|
+
border-radius: 4px;
|
|
2148
|
+
background: var(--glass-bg-2);
|
|
2149
|
+
color: var(--text-dim);
|
|
2150
|
+
padding: 2px 6px;
|
|
2151
|
+
font-size: 10px;
|
|
2152
|
+
cursor: pointer;
|
|
2153
|
+
}
|
|
2154
|
+
.md-code-copy:hover { color: var(--text); }
|
|
2141
2155
|
|
|
2142
2156
|
#chat-area {
|
|
2143
2157
|
flex: 1;
|
|
@@ -2512,6 +2526,33 @@ button.left-ws-item {
|
|
|
2512
2526
|
overflow: visible;
|
|
2513
2527
|
}
|
|
2514
2528
|
|
|
2529
|
+
/* Build Block transcript is a plain timeline, never a glass surface. Keep
|
|
2530
|
+
this explicit and high-priority so broad chat/panel material rules cannot
|
|
2531
|
+
reintroduce a rounded white compositor plane around an expanded run. */
|
|
2532
|
+
.work-run-message,
|
|
2533
|
+
.conversation-work-run,
|
|
2534
|
+
.conversation-work-run-head,
|
|
2535
|
+
.conversation-work-run-body {
|
|
2536
|
+
background: transparent !important;
|
|
2537
|
+
background-image: none !important;
|
|
2538
|
+
backdrop-filter: none !important;
|
|
2539
|
+
-webkit-backdrop-filter: none !important;
|
|
2540
|
+
filter: none !important;
|
|
2541
|
+
border: 0 !important;
|
|
2542
|
+
border-radius: 0 !important;
|
|
2543
|
+
box-shadow: none !important;
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
.work-run-message::before,
|
|
2547
|
+
.work-run-message::after,
|
|
2548
|
+
.conversation-work-run-head::before,
|
|
2549
|
+
.conversation-work-run-head::after,
|
|
2550
|
+
.conversation-work-run-body::before,
|
|
2551
|
+
.conversation-work-run-body::after {
|
|
2552
|
+
content: none !important;
|
|
2553
|
+
display: none !important;
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2515
2556
|
.conversation-work-run::before {
|
|
2516
2557
|
content: "";
|
|
2517
2558
|
position: absolute;
|
|
@@ -2589,8 +2630,8 @@ button.left-ws-item {
|
|
|
2589
2630
|
.conversation-work-activity-list { display: flex; flex-direction: column; gap: 4px; margin: 4px 0 5px 24px; padding: 0; border: 0; }
|
|
2590
2631
|
.conversation-work-activity-item { display: grid; grid-template-columns: 17px minmax(0,1fr); gap: 7px; align-items: start; min-height: 24px; color: var(--text-dim); font: 400 11px/1.5 var(--font-ui); }
|
|
2591
2632
|
.conversation-work-activity-item .nm-icon { margin-top: 2px; }
|
|
2592
|
-
.conversation-work-display-image { display:grid; gap:6px; width:min(100%, 640px); margin:7px 0 3px 24px; padding:0; border:0; background:transparent; color:var(--text-dim); text-align:left; cursor:pointer; }
|
|
2593
|
-
.conversation-work-display-image img { display:block; width:100%; max-height:420px; object-fit:contain; object-position:left center; border:1px solid var(--glass-border-2); border-radius:var(--radius-
|
|
2633
|
+
.conversation-work-display-image { display:inline-grid; justify-items:start; gap:6px; width:fit-content !important; max-width:min(100%, 640px); margin:7px 0 3px 24px; padding:0; border:0; border-radius:0 !important; background:transparent; color:var(--text-dim); text-align:left; cursor:pointer; }
|
|
2634
|
+
.conversation-work-display-image img { display:block; width:auto; max-width:100%; height:auto; max-height:420px; object-fit:contain; object-position:left center; border:1px solid var(--glass-border-2); border-radius:var(--radius-sm); background:rgba(0,0,0,.18); }
|
|
2594
2635
|
.conversation-work-display-image span { font:10px/1.4 var(--font-ui); }
|
|
2595
2636
|
.conversation-work-display-image:hover span, .conversation-work-display-image:focus-visible span { color:var(--accent); }
|
|
2596
2637
|
.work-run-collapsed-images { display:none; flex-direction:column; gap:8px; margin:0 34px 8px 0; }
|
|
@@ -4823,20 +4864,28 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4823
4864
|
}
|
|
4824
4865
|
|
|
4825
4866
|
.conversation-image-attachments {
|
|
4826
|
-
display:
|
|
4827
|
-
|
|
4867
|
+
display: flex;
|
|
4868
|
+
flex-wrap: wrap;
|
|
4869
|
+
align-items: flex-start;
|
|
4870
|
+
justify-content: flex-start;
|
|
4828
4871
|
gap: 8px;
|
|
4829
4872
|
margin-top: 9px;
|
|
4830
4873
|
}
|
|
4874
|
+
.chat-msg.user .conversation-image-attachments { justify-content: flex-end; }
|
|
4875
|
+
.chat-msg.assistant .conversation-image-attachments,
|
|
4876
|
+
.chat-msg.workflow .conversation-image-attachments { justify-content: flex-start; }
|
|
4831
4877
|
|
|
4832
4878
|
.conversation-image-attachment {
|
|
4833
4879
|
min-width: 0;
|
|
4834
|
-
|
|
4880
|
+
width: fit-content !important;
|
|
4881
|
+
max-width: min(100%, 280px);
|
|
4882
|
+
display: inline-flex;
|
|
4835
4883
|
flex-direction: column;
|
|
4884
|
+
align-items: flex-start;
|
|
4836
4885
|
gap: 5px;
|
|
4837
4886
|
padding: 6px;
|
|
4838
4887
|
border: 1px solid var(--glass-border-2);
|
|
4839
|
-
border-radius: var(--radius-
|
|
4888
|
+
border-radius: var(--radius-sm) !important;
|
|
4840
4889
|
background: var(--glass-bg-1);
|
|
4841
4890
|
color: var(--text);
|
|
4842
4891
|
font: 11px/1.35 var(--font-ui);
|
|
@@ -4851,8 +4900,11 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4851
4900
|
}
|
|
4852
4901
|
|
|
4853
4902
|
.conversation-image-attachment img {
|
|
4854
|
-
|
|
4855
|
-
|
|
4903
|
+
display: block;
|
|
4904
|
+
width: auto;
|
|
4905
|
+
max-width: 100%;
|
|
4906
|
+
height: auto;
|
|
4907
|
+
max-height: 180px;
|
|
4856
4908
|
border-radius: var(--radius-sm);
|
|
4857
4909
|
object-fit: contain;
|
|
4858
4910
|
background: rgba(0,0,0,.18);
|
|
@@ -8814,7 +8866,7 @@ function renderMarkdownBlocks(text) {
|
|
|
8814
8866
|
var code = codeLines.join('\n');
|
|
8815
8867
|
var normalizedLanguage = lang ? normalizeCodeLanguage(lang) : 'text';
|
|
8816
8868
|
var highlighted = normalizedLanguage === 'text' ? esc(code) : highlightCodeByLanguage(code, normalizedLanguage);
|
|
8817
|
-
html += '<pre><code' + (lang ? ' data-lang="' + escAttr(lang) + '"' : '') + ' class="md-code-block language-' + escAttr(normalizedLanguage) + '">' + highlighted + '</code></pre>';
|
|
8869
|
+
html += '<pre><button class="md-code-copy" type="button" title="' + escAttr(t('common.copy')) + '" data-code="' + escAttr(code) + '" onclick="window.copyMarkdownCode(this)">' + esc(t('common.copy')) + '</button><code' + (lang ? ' data-lang="' + escAttr(lang) + '"' : '') + ' class="md-code-block language-' + escAttr(normalizedLanguage) + '">' + highlighted + '</code></pre>';
|
|
8818
8870
|
continue;
|
|
8819
8871
|
}
|
|
8820
8872
|
if (trimmed === '$$' || trimmed === '\\[') {
|
|
@@ -9891,6 +9943,19 @@ function workReviewRunIds(runId, aliases) {
|
|
|
9891
9943
|
return ids;
|
|
9892
9944
|
}
|
|
9893
9945
|
|
|
9946
|
+
window.copyMarkdownCode = async function(button) {
|
|
9947
|
+
var value = String(button && button.getAttribute('data-code') || '');
|
|
9948
|
+
try {
|
|
9949
|
+
if (navigator.clipboard && navigator.clipboard.writeText) await navigator.clipboard.writeText(value);
|
|
9950
|
+
else {
|
|
9951
|
+
var helper = document.createElement('textarea');
|
|
9952
|
+
helper.value = value; helper.style.position = 'fixed'; helper.style.opacity = '0';
|
|
9953
|
+
document.body.appendChild(helper); helper.select(); document.execCommand('copy'); helper.remove();
|
|
9954
|
+
}
|
|
9955
|
+
if (button) { button.textContent = t('common.copied'); setTimeout(function() { button.textContent = t('common.copy'); }, 1200); }
|
|
9956
|
+
} catch (e) {}
|
|
9957
|
+
};
|
|
9958
|
+
|
|
9894
9959
|
function upsertWorkReviewRecord(ui, diffs, runId, aliases) {
|
|
9895
9960
|
if (!ui) return null;
|
|
9896
9961
|
if (!Array.isArray(ui.workReviews)) ui.workReviews = [];
|
|
@@ -12359,6 +12424,33 @@ function replayActiveAgentWorkEvents() {
|
|
|
12359
12424
|
}
|
|
12360
12425
|
|
|
12361
12426
|
var _conversationsRenderPending = false;
|
|
12427
|
+
var _contextWindowRefreshTimer = null;
|
|
12428
|
+
var _contextWindowRefreshInFlight = false;
|
|
12429
|
+
var _contextWindowRefreshTargetKey = '';
|
|
12430
|
+
|
|
12431
|
+
function scheduleActiveContextWindowRefresh(target) {
|
|
12432
|
+
if (!api.getState || !target || !isActiveConversationTarget(target)) return;
|
|
12433
|
+
var targetKey = runtimeKeyFor(target.workspaceId, target.conversationId);
|
|
12434
|
+
_contextWindowRefreshTargetKey = targetKey;
|
|
12435
|
+
if (_contextWindowRefreshTimer !== null) return;
|
|
12436
|
+
_contextWindowRefreshTimer = window.setTimeout(function() {
|
|
12437
|
+
_contextWindowRefreshTimer = null;
|
|
12438
|
+
if (_contextWindowRefreshInFlight || !isActiveConversationTarget(target)) return;
|
|
12439
|
+
_contextWindowRefreshInFlight = true;
|
|
12440
|
+
api.getState(target).then(function(snapshot) {
|
|
12441
|
+
if (!snapshot || !isActiveConversationTarget(target) || _contextWindowRefreshTargetKey !== targetKey) return;
|
|
12442
|
+
if (snapshot.contextWindow !== undefined) {
|
|
12443
|
+
state.contextWindow = snapshot.contextWindow;
|
|
12444
|
+
window.renderContextWindow();
|
|
12445
|
+
}
|
|
12446
|
+
if (snapshot.contextCompression !== undefined) state.contextCompression = snapshot.contextCompression;
|
|
12447
|
+
if (state.rightTab === 'status') window.renderRightStatusPanel();
|
|
12448
|
+
}).catch(function(){}).then(function() {
|
|
12449
|
+
_contextWindowRefreshInFlight = false;
|
|
12450
|
+
});
|
|
12451
|
+
}, 120);
|
|
12452
|
+
}
|
|
12453
|
+
|
|
12362
12454
|
function scheduleConversationsRender() {
|
|
12363
12455
|
if (_conversationsRenderPending) return;
|
|
12364
12456
|
_conversationsRenderPending = true;
|
|
@@ -12426,6 +12518,15 @@ function appendAgentWorkEvent(event) {
|
|
|
12426
12518
|
if (active) {
|
|
12427
12519
|
setWorking(!!runningConversationRecord(activeConversationId()) || currentFlowRunning());
|
|
12428
12520
|
updateSubmitButtonState();
|
|
12521
|
+
// Refresh snapshots only at semantic boundaries. Text/thought deltas can
|
|
12522
|
+
// arrive hundreds of times per second; issuing IPC snapshots for each
|
|
12523
|
+
// delta creates avoidable renderer/runtime contention and used to make a
|
|
12524
|
+
// healthy utility runtime look like a mysteriously interrupted run.
|
|
12525
|
+
var refreshableLiveEvent = event.type === 'status'
|
|
12526
|
+
|| event.type === 'tool_result'
|
|
12527
|
+
|| event.type === 'usage'
|
|
12528
|
+
|| event.type === 'message_end';
|
|
12529
|
+
if (refreshableLiveEvent) scheduleActiveContextWindowRefresh(target);
|
|
12429
12530
|
}
|
|
12430
12531
|
if (active && event.type === 'tool_result' && ['linked_plan', 'task_read', 'task_create', 'task', 'subagent_create', 'SubAgent', 'subagent_send', 'subagent_read', 'subagent_close'].indexOf(String(event.toolName || '')) >= 0 && api.getState) {
|
|
12431
12532
|
var eventWorkspaceKey = currentWorkspaceKey();
|
|
@@ -12441,6 +12542,12 @@ function appendAgentWorkEvent(event) {
|
|
|
12441
12542
|
var terminalWorkspaceKey = currentWorkspaceKey();
|
|
12442
12543
|
api.getState(target).then(function(snapshot) {
|
|
12443
12544
|
if (!isActiveConversationTarget(target) || terminalWorkspaceKey !== currentWorkspaceKey()) return;
|
|
12545
|
+
if (snapshot && snapshot.contextWindow !== undefined) {
|
|
12546
|
+
state.contextWindow = snapshot.contextWindow;
|
|
12547
|
+
window.renderContextWindow();
|
|
12548
|
+
}
|
|
12549
|
+
if (snapshot && snapshot.contextCompression !== undefined) state.contextCompression = snapshot.contextCompression;
|
|
12550
|
+
if (state.rightTab === 'status') window.renderRightStatusPanel();
|
|
12444
12551
|
// Backend-owned Goal continuations have no renderer send promise to
|
|
12445
12552
|
// return the final snapshot. Refresh on the terminal work event so a
|
|
12446
12553
|
// completed Goal clears its bar and restores Build immediately.
|
|
@@ -21564,6 +21671,18 @@ function initializeBrowserGuest(view) {
|
|
|
21564
21671
|
addMsg('assistant', '[Browser] Load failed: ' + e.errorDescription, 'error', '');
|
|
21565
21672
|
}
|
|
21566
21673
|
});
|
|
21674
|
+
// Keep target=_blank/window.open inside the embedded browser. Electron's
|
|
21675
|
+
// webview otherwise creates an external window that has no address bar or
|
|
21676
|
+
// reload control, so a popup page could not be refreshed in-place.
|
|
21677
|
+
view.addEventListener('new-window', function(e) {
|
|
21678
|
+
var popupUrl = String(e && e.url || '').trim();
|
|
21679
|
+
if (!popupUrl || !/^(?:https?:|about:blank|newmark-preview:)/i.test(popupUrl)) return;
|
|
21680
|
+
if (e && typeof e.preventDefault === 'function') e.preventDefault();
|
|
21681
|
+
rememberBrowserUrl(popupUrl, true, target);
|
|
21682
|
+
var input = els['browser-url'];
|
|
21683
|
+
if (input && isActiveConversationTarget(target)) input.value = popupUrl;
|
|
21684
|
+
navigateBrowserView(view, popupUrl);
|
|
21685
|
+
});
|
|
21567
21686
|
var current = '';
|
|
21568
21687
|
try { current = view.getURL ? view.getURL() : ''; } catch { current = ''; }
|
|
21569
21688
|
if (!current && !view.getAttribute('src')) view.setAttribute('src', browserRetainedUrls[targetKey] || 'about:blank');
|
|
@@ -8383,7 +8383,7 @@ var require_createWorker = __commonJS({
|
|
|
8383
8383
|
...defaultOptions,
|
|
8384
8384
|
..._options
|
|
8385
8385
|
});
|
|
8386
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
8512
|
-
delete
|
|
8511
|
+
promises3[promiseId].resolve({ jobId, data });
|
|
8512
|
+
delete promises3[promiseId];
|
|
8513
8513
|
} else if (status === "reject") {
|
|
8514
|
-
|
|
8515
|
-
delete
|
|
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
|
|
54009
|
+
const promises3 = [this[kAgent].close()];
|
|
54010
54010
|
if (this[kClient]) {
|
|
54011
|
-
|
|
54011
|
+
promises3.push(this[kClient].close());
|
|
54012
54012
|
}
|
|
54013
|
-
return Promise.all(
|
|
54013
|
+
return Promise.all(promises3);
|
|
54014
54014
|
}
|
|
54015
54015
|
[kDestroy]() {
|
|
54016
|
-
const
|
|
54016
|
+
const promises3 = [this[kAgent].destroy()];
|
|
54017
54017
|
if (this[kClient]) {
|
|
54018
|
-
|
|
54018
|
+
promises3.push(this[kClient].destroy());
|
|
54019
54019
|
}
|
|
54020
|
-
return Promise.all(
|
|
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
|
-
|
|
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
|
|
|
@@ -348526,12 +348528,15 @@ Conversation title (a few words):`;
|
|
|
348526
348528
|
defaultInputMode() {
|
|
348527
348529
|
return this.config.getStr("general", "default_input") === "next" ? "next" : "guide";
|
|
348528
348530
|
}
|
|
348529
|
-
abortActiveKernelRun() {
|
|
348531
|
+
abortActiveKernelRun(reason = "unspecified") {
|
|
348530
348532
|
let aborted = false;
|
|
348531
348533
|
this.subagents.pauseScheduling();
|
|
348532
348534
|
if (this.activeProcessAbortController && !this.activeProcessAbortController.signal.aborted) {
|
|
348533
348535
|
const abortError5 = new Error("Agent run aborted");
|
|
348534
348536
|
abortError5.name = "AbortError";
|
|
348537
|
+
if (process.env.NEWMARK_PROVIDER_DIAGNOSTICS === "1") {
|
|
348538
|
+
console.error(`[NewmarkKernel] abort-request reason=${String(reason).slice(0, 160)} conversation=${this.activeConversationId}`);
|
|
348539
|
+
}
|
|
348535
348540
|
this.activeProcessAbortController.abort(abortError5);
|
|
348536
348541
|
aborted = true;
|
|
348537
348542
|
}
|
|
@@ -348540,7 +348545,7 @@ Conversation title (a few words):`;
|
|
|
348540
348545
|
aborted = true;
|
|
348541
348546
|
}
|
|
348542
348547
|
for (const peer of this.activePeerAgents.values()) {
|
|
348543
|
-
aborted = peer.abortActiveKernelRun() || aborted;
|
|
348548
|
+
aborted = peer.abortActiveKernelRun(reason) || aborted;
|
|
348544
348549
|
}
|
|
348545
348550
|
this.pendingAgentKernelQueue = [];
|
|
348546
348551
|
return aborted;
|
|
@@ -350047,10 +350052,16 @@ ${summary}`, segment, "local-summarize", true);
|
|
|
350047
350052
|
return { compressed: true, rounds, segments, droppedMessages, estimatedTokens: this.estimateContextTokens(candidate), maxTokens };
|
|
350048
350053
|
}
|
|
350049
350054
|
compactHistoricalImages(messages) {
|
|
350055
|
+
const continuationIndex = messages.reduce((found, message, index) => {
|
|
350056
|
+
const content = String(message?.content || "");
|
|
350057
|
+
return content.includes("[Post-Compression Task Continuation]") ? index : found;
|
|
350058
|
+
}, -1);
|
|
350050
350059
|
const lastParts = Array.isArray(messages.at(-1)?.content) ? messages.at(-1).content : [];
|
|
350051
|
-
const newestImageMessage = lastParts.some((part) => part?.type === "image_url") ? messages.length - 1 : -1;
|
|
350060
|
+
const newestImageMessage = continuationIndex < 0 && lastParts.some((part) => part?.type === "image_url") ? messages.length - 1 : -1;
|
|
350052
350061
|
return messages.map((message, index) => {
|
|
350053
|
-
if (!Array.isArray(message.content)
|
|
350062
|
+
if (!Array.isArray(message.content)) return { ...message };
|
|
350063
|
+
const preserveRecentImage = continuationIndex >= 0 && index > continuationIndex;
|
|
350064
|
+
if (preserveRecentImage || index === newestImageMessage) return { ...message };
|
|
350054
350065
|
const parts = message.content.flatMap((part) => {
|
|
350055
350066
|
if (part?.type !== "image_url") return [{ ...part }];
|
|
350056
350067
|
return [{ type: "text", text: "[Historical image attachment omitted after context compression.]" }];
|
|
@@ -352974,12 +352985,16 @@ ${text.slice(-tailChars).trimStart()}`;
|
|
|
352974
352985
|
}
|
|
352975
352986
|
buildSystemPrompt() {
|
|
352976
352987
|
const cwd = this.workspace.current?.path || this.rootPath;
|
|
352988
|
+
const newmarkConfigRoot = path28.resolve(this.rootPath);
|
|
352989
|
+
const newmarkConfigFile = path28.join(newmarkConfigRoot, "config.json");
|
|
352977
352990
|
const enabledSkills = this.skills.active();
|
|
352978
352991
|
const globalPromptPath = path28.join(this.rootPath, "agent.md");
|
|
352979
352992
|
const globalPrompt = normalizeInjectedPrompt(fs25.existsSync(globalPromptPath) ? fs25.readFileSync(globalPromptPath, "utf-8") : "");
|
|
352980
352993
|
const workspacePrompt = normalizeInjectedPrompt(this.workspace.currentAgentPrompt());
|
|
352981
352994
|
const identity = JSON.stringify({
|
|
352982
352995
|
cwd,
|
|
352996
|
+
newmarkConfigRoot,
|
|
352997
|
+
newmarkConfigFile,
|
|
352983
352998
|
mode: this.mode,
|
|
352984
352999
|
conversationId: this.activeConversationId,
|
|
352985
353000
|
subagent: this.isSubagentRuntime ? [this.subagentName, this.subagentPrompt] : null,
|
|
@@ -352998,6 +353013,9 @@ ${text.slice(-tailChars).trimStart()}`;
|
|
|
352998
353013
|
if (this.systemPromptCache?.identity === identity) return this.systemPromptCache.value;
|
|
352999
353014
|
const parts = [`${CORE_SYSTEM_PROMPT}
|
|
353000
353015
|
|
|
353016
|
+
## Newmark Runtime Configuration
|
|
353017
|
+
Newmark's own user-level configuration and runtime state are stored by default under ${newmarkConfigRoot} (the conventional user path is user/.Newmark). The primary configuration file is ${newmarkConfigFile}; provider credentials, conversation state, caches, Work/Flow data, Memory Lab, skills, and archives may also live beneath this root. Treat these files as Newmark internal state: read or modify them only when the user explicitly asks, never expose API keys or tokens, and do not confuse this runtime root with the active workspace.
|
|
353018
|
+
|
|
353001
353019
|
## Current Working Directory
|
|
353002
353020
|
${cwd}
|
|
353003
353021
|
|
|
@@ -353743,7 +353761,12 @@ var ConversationKernel = class {
|
|
|
353743
353761
|
...images?.length ? { images } : {},
|
|
353744
353762
|
...attachments?.length ? { attachments } : {},
|
|
353745
353763
|
...visible ? { visibleUserInput: visible } : {},
|
|
353746
|
-
...visibleMode ? { visibleMode } : {}
|
|
353764
|
+
...visibleMode ? { visibleMode } : {},
|
|
353765
|
+
// A normal user follow-up must never inherit the identity metadata that
|
|
353766
|
+
// was only needed while it lived in the runtime queue. In particular,
|
|
353767
|
+
// do not let a stale/forwarded hiddenUserInput flag classify it as an
|
|
353768
|
+
// Agent-generated continuation when Agent.process persists the turn.
|
|
353769
|
+
hiddenUserInput: message.hiddenUserInput === true && !message.clientMessageId
|
|
353747
353770
|
};
|
|
353748
353771
|
}
|
|
353749
353772
|
queueItems(target) {
|
|
@@ -354316,7 +354339,7 @@ ${item.goalObjective}` : item.text,
|
|
|
354316
354339
|
this.deferOutstandingGuides(runtime);
|
|
354317
354340
|
const checkpointed = this.checkpoint(runtime.target).checkpointed;
|
|
354318
354341
|
runtime.stopCheckpointed = checkpointed;
|
|
354319
|
-
runtime.runner.abortActiveKernelRun();
|
|
354342
|
+
runtime.runner.abortActiveKernelRun("user_stop");
|
|
354320
354343
|
runtime.runner.emitWorkEvent({
|
|
354321
354344
|
type: "status",
|
|
354322
354345
|
content: "Stop requested. Saving progress and interrupting this conversation.",
|
|
@@ -354450,6 +354473,7 @@ ${item.goalObjective}` : item.text,
|
|
|
354450
354473
|
async run(runtime, message, options) {
|
|
354451
354474
|
this.applyOptions(runtime.runner, options);
|
|
354452
354475
|
let lastTokens = await this.runSingle(runtime, message);
|
|
354476
|
+
this.rememberAutomaticAssistantFingerprint(runtime, message);
|
|
354453
354477
|
if (runtime.stopRequestedRunId === runtime.runId) {
|
|
354454
354478
|
this.mirrorHostIfTargetActive(runtime);
|
|
354455
354479
|
return this.result(runtime, lastTokens);
|
|
@@ -354476,6 +354500,7 @@ ${item.goalObjective}` : item.text,
|
|
|
354476
354500
|
}
|
|
354477
354501
|
if (batchGuides.length === 1) {
|
|
354478
354502
|
lastTokens = await this.runSingle(runtime, next.message, next.queueMode);
|
|
354503
|
+
if (this.repeatedAutomaticAssistant(runtime, next.message, next.queueMode)) break;
|
|
354479
354504
|
continue;
|
|
354480
354505
|
}
|
|
354481
354506
|
const batchText = batchGuides.map((guide, index) => `Guide ${index + 1}: ${guide.text}`).join("\n");
|
|
@@ -354486,8 +354511,11 @@ ${batchText}`,
|
|
|
354486
354511
|
batchGuides
|
|
354487
354512
|
};
|
|
354488
354513
|
lastTokens = await this.runSingle(runtime, batchMessage, "steer");
|
|
354514
|
+
if (this.repeatedAutomaticAssistant(runtime, batchMessage, "steer")) break;
|
|
354489
354515
|
} else {
|
|
354490
|
-
|
|
354516
|
+
const drained = this.drainQueuedFollowUpMessage(next.message);
|
|
354517
|
+
lastTokens = await this.runSingle(runtime, drained, next.queueMode);
|
|
354518
|
+
if (this.repeatedAutomaticAssistant(runtime, drained, next.queueMode)) break;
|
|
354491
354519
|
}
|
|
354492
354520
|
}
|
|
354493
354521
|
const rootMessage = runtime.runner.subagents.readRootInbox()[0];
|
|
@@ -354522,6 +354550,35 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354522
354550
|
this.mirrorHostIfTargetActive(runtime);
|
|
354523
354551
|
return this.result(runtime, lastTokens);
|
|
354524
354552
|
}
|
|
354553
|
+
automaticMessage(message, queueMode) {
|
|
354554
|
+
return queueMode === "steer" || typeof message !== "string" && (message.hiddenUserInput === true || message.goalContinuation === true || !!message.batchGuides?.length);
|
|
354555
|
+
}
|
|
354556
|
+
currentAssistantFingerprint(runtime) {
|
|
354557
|
+
const messages = runtime.runner.chatMessages || [];
|
|
354558
|
+
const last = messages[messages.length - 1];
|
|
354559
|
+
if (!last || last.role !== "assistant") return "";
|
|
354560
|
+
return String(last.content || "").trim().replace(/\s+/g, " ").toLowerCase().slice(0, 4e3);
|
|
354561
|
+
}
|
|
354562
|
+
rememberAutomaticAssistantFingerprint(runtime, message, queueMode) {
|
|
354563
|
+
if (this.automaticMessage(message, queueMode)) runtime.lastAutomaticAssistantFingerprint = this.currentAssistantFingerprint(runtime) || void 0;
|
|
354564
|
+
}
|
|
354565
|
+
repeatedAutomaticAssistant(runtime, message, queueMode) {
|
|
354566
|
+
if (!this.automaticMessage(message, queueMode)) return false;
|
|
354567
|
+
const fingerprint2 = this.currentAssistantFingerprint(runtime);
|
|
354568
|
+
if (!fingerprint2) return false;
|
|
354569
|
+
if (runtime.lastAutomaticAssistantFingerprint === fingerprint2) {
|
|
354570
|
+
runtime.pendingNextTurn = runtime.pendingNextTurn.filter((item) => {
|
|
354571
|
+
const automatic = item.queueMode === "steer" || typeof item.message !== "string" && item.message.hiddenUserInput === true;
|
|
354572
|
+
if (automatic) runtime.runner.consumeConversationContinuation({ content: typeof item.message === "string" ? item.message : item.message.text, queueMode: item.queueMode, clientMessageId: typeof item.message === "string" ? void 0 : item.message.clientMessageId });
|
|
354573
|
+
return !automatic;
|
|
354574
|
+
});
|
|
354575
|
+
runtime.runner.recordWorkStatus("Automatic continuation stopped after a repeated assistant response.");
|
|
354576
|
+
this.emitQueueUpdate(runtime);
|
|
354577
|
+
return true;
|
|
354578
|
+
}
|
|
354579
|
+
runtime.lastAutomaticAssistantFingerprint = fingerprint2;
|
|
354580
|
+
return false;
|
|
354581
|
+
}
|
|
354525
354582
|
/**
|
|
354526
354583
|
* Apply a model selection recorded while a Build block was running. The
|
|
354527
354584
|
* in-flight block never switches mid-block; the switch takes effect the next
|
|
@@ -354561,7 +354618,16 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354561
354618
|
tokens = await Promise.race([
|
|
354562
354619
|
runtime.runner.process(message),
|
|
354563
354620
|
new Promise((_3, reject) => {
|
|
354564
|
-
timeout = setTimeout(() =>
|
|
354621
|
+
timeout = setTimeout(() => {
|
|
354622
|
+
runtime.runner.abortActiveKernelRun(`process_timeout_${timeoutMs}ms`);
|
|
354623
|
+
runtime.runner.emitWorkEvent({
|
|
354624
|
+
type: "error",
|
|
354625
|
+
content: `Process timeout (${Math.round(timeoutMs / 1e3)}s); the run was aborted to prevent a stale worker from continuing.`,
|
|
354626
|
+
status: "error",
|
|
354627
|
+
runId: runtime.runId
|
|
354628
|
+
});
|
|
354629
|
+
reject(new Error(`Process timeout (${Math.round(timeoutMs / 1e3)}s); run aborted and checkpointed`));
|
|
354630
|
+
}, timeoutMs);
|
|
354565
354631
|
})
|
|
354566
354632
|
]);
|
|
354567
354633
|
} catch (error) {
|
|
@@ -354622,7 +354688,8 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354622
354688
|
guideReceipts: /* @__PURE__ */ new Map(),
|
|
354623
354689
|
guideEnvelopes: /* @__PURE__ */ new Map(),
|
|
354624
354690
|
goalContinuationTimer: void 0,
|
|
354625
|
-
pendingContinuationRunId: void 0
|
|
354691
|
+
pendingContinuationRunId: void 0,
|
|
354692
|
+
lastAutomaticAssistantFingerprint: void 0
|
|
354626
354693
|
};
|
|
354627
354694
|
runner.setGoalContinuationGate(() => {
|
|
354628
354695
|
this.queueState(runtime);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newmark-agent",
|
|
3
3
|
"productName": "Newmark Agent",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.12",
|
|
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",
|