newmark-agent 0.5.11 → 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 +74 -9
- 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/ui/index.html +128 -9
- package/dist/wsl-agent-host.bundle.cjs +74 -9
- package/package.json +1 -1
|
@@ -348524,12 +348524,15 @@ Conversation title (a few words):`;
|
|
|
348524
348524
|
defaultInputMode() {
|
|
348525
348525
|
return this.config.getStr("general", "default_input") === "next" ? "next" : "guide";
|
|
348526
348526
|
}
|
|
348527
|
-
abortActiveKernelRun() {
|
|
348527
|
+
abortActiveKernelRun(reason = "unspecified") {
|
|
348528
348528
|
let aborted = false;
|
|
348529
348529
|
this.subagents.pauseScheduling();
|
|
348530
348530
|
if (this.activeProcessAbortController && !this.activeProcessAbortController.signal.aborted) {
|
|
348531
348531
|
const abortError5 = new Error("Agent run aborted");
|
|
348532
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
|
+
}
|
|
348533
348536
|
this.activeProcessAbortController.abort(abortError5);
|
|
348534
348537
|
aborted = true;
|
|
348535
348538
|
}
|
|
@@ -348538,7 +348541,7 @@ Conversation title (a few words):`;
|
|
|
348538
348541
|
aborted = true;
|
|
348539
348542
|
}
|
|
348540
348543
|
for (const peer of this.activePeerAgents.values()) {
|
|
348541
|
-
aborted = peer.abortActiveKernelRun() || aborted;
|
|
348544
|
+
aborted = peer.abortActiveKernelRun(reason) || aborted;
|
|
348542
348545
|
}
|
|
348543
348546
|
this.pendingAgentKernelQueue = [];
|
|
348544
348547
|
return aborted;
|
|
@@ -350045,10 +350048,16 @@ ${summary}`, segment, "local-summarize", true);
|
|
|
350045
350048
|
return { compressed: true, rounds, segments, droppedMessages, estimatedTokens: this.estimateContextTokens(candidate), maxTokens };
|
|
350046
350049
|
}
|
|
350047
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);
|
|
350048
350055
|
const lastParts = Array.isArray(messages.at(-1)?.content) ? messages.at(-1).content : [];
|
|
350049
|
-
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;
|
|
350050
350057
|
return messages.map((message, index) => {
|
|
350051
|
-
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 };
|
|
350052
350061
|
const parts = message.content.flatMap((part) => {
|
|
350053
350062
|
if (part?.type !== "image_url") return [{ ...part }];
|
|
350054
350063
|
return [{ type: "text", text: "[Historical image attachment omitted after context compression.]" }];
|
|
@@ -352972,12 +352981,16 @@ ${text.slice(-tailChars).trimStart()}`;
|
|
|
352972
352981
|
}
|
|
352973
352982
|
buildSystemPrompt() {
|
|
352974
352983
|
const cwd = this.workspace.current?.path || this.rootPath;
|
|
352984
|
+
const newmarkConfigRoot = path28.resolve(this.rootPath);
|
|
352985
|
+
const newmarkConfigFile = path28.join(newmarkConfigRoot, "config.json");
|
|
352975
352986
|
const enabledSkills = this.skills.active();
|
|
352976
352987
|
const globalPromptPath = path28.join(this.rootPath, "agent.md");
|
|
352977
352988
|
const globalPrompt = normalizeInjectedPrompt(fs25.existsSync(globalPromptPath) ? fs25.readFileSync(globalPromptPath, "utf-8") : "");
|
|
352978
352989
|
const workspacePrompt = normalizeInjectedPrompt(this.workspace.currentAgentPrompt());
|
|
352979
352990
|
const identity = JSON.stringify({
|
|
352980
352991
|
cwd,
|
|
352992
|
+
newmarkConfigRoot,
|
|
352993
|
+
newmarkConfigFile,
|
|
352981
352994
|
mode: this.mode,
|
|
352982
352995
|
conversationId: this.activeConversationId,
|
|
352983
352996
|
subagent: this.isSubagentRuntime ? [this.subagentName, this.subagentPrompt] : null,
|
|
@@ -352996,6 +353009,9 @@ ${text.slice(-tailChars).trimStart()}`;
|
|
|
352996
353009
|
if (this.systemPromptCache?.identity === identity) return this.systemPromptCache.value;
|
|
352997
353010
|
const parts = [`${CORE_SYSTEM_PROMPT}
|
|
352998
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
|
+
|
|
352999
353015
|
## Current Working Directory
|
|
353000
353016
|
${cwd}
|
|
353001
353017
|
|
|
@@ -353741,7 +353757,12 @@ var ConversationKernel = class {
|
|
|
353741
353757
|
...images?.length ? { images } : {},
|
|
353742
353758
|
...attachments?.length ? { attachments } : {},
|
|
353743
353759
|
...visible ? { visibleUserInput: visible } : {},
|
|
353744
|
-
...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
|
|
353745
353766
|
};
|
|
353746
353767
|
}
|
|
353747
353768
|
queueItems(target) {
|
|
@@ -354314,7 +354335,7 @@ ${item.goalObjective}` : item.text,
|
|
|
354314
354335
|
this.deferOutstandingGuides(runtime);
|
|
354315
354336
|
const checkpointed = this.checkpoint(runtime.target).checkpointed;
|
|
354316
354337
|
runtime.stopCheckpointed = checkpointed;
|
|
354317
|
-
runtime.runner.abortActiveKernelRun();
|
|
354338
|
+
runtime.runner.abortActiveKernelRun("user_stop");
|
|
354318
354339
|
runtime.runner.emitWorkEvent({
|
|
354319
354340
|
type: "status",
|
|
354320
354341
|
content: "Stop requested. Saving progress and interrupting this conversation.",
|
|
@@ -354448,6 +354469,7 @@ ${item.goalObjective}` : item.text,
|
|
|
354448
354469
|
async run(runtime, message, options) {
|
|
354449
354470
|
this.applyOptions(runtime.runner, options);
|
|
354450
354471
|
let lastTokens = await this.runSingle(runtime, message);
|
|
354472
|
+
this.rememberAutomaticAssistantFingerprint(runtime, message);
|
|
354451
354473
|
if (runtime.stopRequestedRunId === runtime.runId) {
|
|
354452
354474
|
this.mirrorHostIfTargetActive(runtime);
|
|
354453
354475
|
return this.result(runtime, lastTokens);
|
|
@@ -354474,6 +354496,7 @@ ${item.goalObjective}` : item.text,
|
|
|
354474
354496
|
}
|
|
354475
354497
|
if (batchGuides.length === 1) {
|
|
354476
354498
|
lastTokens = await this.runSingle(runtime, next.message, next.queueMode);
|
|
354499
|
+
if (this.repeatedAutomaticAssistant(runtime, next.message, next.queueMode)) break;
|
|
354477
354500
|
continue;
|
|
354478
354501
|
}
|
|
354479
354502
|
const batchText = batchGuides.map((guide, index) => `Guide ${index + 1}: ${guide.text}`).join("\n");
|
|
@@ -354484,8 +354507,11 @@ ${batchText}`,
|
|
|
354484
354507
|
batchGuides
|
|
354485
354508
|
};
|
|
354486
354509
|
lastTokens = await this.runSingle(runtime, batchMessage, "steer");
|
|
354510
|
+
if (this.repeatedAutomaticAssistant(runtime, batchMessage, "steer")) break;
|
|
354487
354511
|
} else {
|
|
354488
|
-
|
|
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;
|
|
354489
354515
|
}
|
|
354490
354516
|
}
|
|
354491
354517
|
const rootMessage = runtime.runner.subagents.readRootInbox()[0];
|
|
@@ -354520,6 +354546,35 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354520
354546
|
this.mirrorHostIfTargetActive(runtime);
|
|
354521
354547
|
return this.result(runtime, lastTokens);
|
|
354522
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
|
+
}
|
|
354523
354578
|
/**
|
|
354524
354579
|
* Apply a model selection recorded while a Build block was running. The
|
|
354525
354580
|
* in-flight block never switches mid-block; the switch takes effect the next
|
|
@@ -354559,7 +354614,16 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354559
354614
|
tokens = await Promise.race([
|
|
354560
354615
|
runtime.runner.process(message),
|
|
354561
354616
|
new Promise((_3, reject) => {
|
|
354562
|
-
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);
|
|
354563
354627
|
})
|
|
354564
354628
|
]);
|
|
354565
354629
|
} catch (error) {
|
|
@@ -354620,7 +354684,8 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354620
354684
|
guideReceipts: /* @__PURE__ */ new Map(),
|
|
354621
354685
|
guideEnvelopes: /* @__PURE__ */ new Map(),
|
|
354622
354686
|
goalContinuationTimer: void 0,
|
|
354623
|
-
pendingContinuationRunId: void 0
|
|
354687
|
+
pendingContinuationRunId: void 0,
|
|
354688
|
+
lastAutomaticAssistantFingerprint: void 0
|
|
354624
354689
|
};
|
|
354625
354690
|
runner.setGoalContinuationGate(() => {
|
|
354626
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) {
|
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');
|
|
@@ -348528,12 +348528,15 @@ Conversation title (a few words):`;
|
|
|
348528
348528
|
defaultInputMode() {
|
|
348529
348529
|
return this.config.getStr("general", "default_input") === "next" ? "next" : "guide";
|
|
348530
348530
|
}
|
|
348531
|
-
abortActiveKernelRun() {
|
|
348531
|
+
abortActiveKernelRun(reason = "unspecified") {
|
|
348532
348532
|
let aborted = false;
|
|
348533
348533
|
this.subagents.pauseScheduling();
|
|
348534
348534
|
if (this.activeProcessAbortController && !this.activeProcessAbortController.signal.aborted) {
|
|
348535
348535
|
const abortError5 = new Error("Agent run aborted");
|
|
348536
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
|
+
}
|
|
348537
348540
|
this.activeProcessAbortController.abort(abortError5);
|
|
348538
348541
|
aborted = true;
|
|
348539
348542
|
}
|
|
@@ -348542,7 +348545,7 @@ Conversation title (a few words):`;
|
|
|
348542
348545
|
aborted = true;
|
|
348543
348546
|
}
|
|
348544
348547
|
for (const peer of this.activePeerAgents.values()) {
|
|
348545
|
-
aborted = peer.abortActiveKernelRun() || aborted;
|
|
348548
|
+
aborted = peer.abortActiveKernelRun(reason) || aborted;
|
|
348546
348549
|
}
|
|
348547
348550
|
this.pendingAgentKernelQueue = [];
|
|
348548
348551
|
return aborted;
|
|
@@ -350049,10 +350052,16 @@ ${summary}`, segment, "local-summarize", true);
|
|
|
350049
350052
|
return { compressed: true, rounds, segments, droppedMessages, estimatedTokens: this.estimateContextTokens(candidate), maxTokens };
|
|
350050
350053
|
}
|
|
350051
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);
|
|
350052
350059
|
const lastParts = Array.isArray(messages.at(-1)?.content) ? messages.at(-1).content : [];
|
|
350053
|
-
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;
|
|
350054
350061
|
return messages.map((message, index) => {
|
|
350055
|
-
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 };
|
|
350056
350065
|
const parts = message.content.flatMap((part) => {
|
|
350057
350066
|
if (part?.type !== "image_url") return [{ ...part }];
|
|
350058
350067
|
return [{ type: "text", text: "[Historical image attachment omitted after context compression.]" }];
|
|
@@ -352976,12 +352985,16 @@ ${text.slice(-tailChars).trimStart()}`;
|
|
|
352976
352985
|
}
|
|
352977
352986
|
buildSystemPrompt() {
|
|
352978
352987
|
const cwd = this.workspace.current?.path || this.rootPath;
|
|
352988
|
+
const newmarkConfigRoot = path28.resolve(this.rootPath);
|
|
352989
|
+
const newmarkConfigFile = path28.join(newmarkConfigRoot, "config.json");
|
|
352979
352990
|
const enabledSkills = this.skills.active();
|
|
352980
352991
|
const globalPromptPath = path28.join(this.rootPath, "agent.md");
|
|
352981
352992
|
const globalPrompt = normalizeInjectedPrompt(fs25.existsSync(globalPromptPath) ? fs25.readFileSync(globalPromptPath, "utf-8") : "");
|
|
352982
352993
|
const workspacePrompt = normalizeInjectedPrompt(this.workspace.currentAgentPrompt());
|
|
352983
352994
|
const identity = JSON.stringify({
|
|
352984
352995
|
cwd,
|
|
352996
|
+
newmarkConfigRoot,
|
|
352997
|
+
newmarkConfigFile,
|
|
352985
352998
|
mode: this.mode,
|
|
352986
352999
|
conversationId: this.activeConversationId,
|
|
352987
353000
|
subagent: this.isSubagentRuntime ? [this.subagentName, this.subagentPrompt] : null,
|
|
@@ -353000,6 +353013,9 @@ ${text.slice(-tailChars).trimStart()}`;
|
|
|
353000
353013
|
if (this.systemPromptCache?.identity === identity) return this.systemPromptCache.value;
|
|
353001
353014
|
const parts = [`${CORE_SYSTEM_PROMPT}
|
|
353002
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
|
+
|
|
353003
353019
|
## Current Working Directory
|
|
353004
353020
|
${cwd}
|
|
353005
353021
|
|
|
@@ -353745,7 +353761,12 @@ var ConversationKernel = class {
|
|
|
353745
353761
|
...images?.length ? { images } : {},
|
|
353746
353762
|
...attachments?.length ? { attachments } : {},
|
|
353747
353763
|
...visible ? { visibleUserInput: visible } : {},
|
|
353748
|
-
...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
|
|
353749
353770
|
};
|
|
353750
353771
|
}
|
|
353751
353772
|
queueItems(target) {
|
|
@@ -354318,7 +354339,7 @@ ${item.goalObjective}` : item.text,
|
|
|
354318
354339
|
this.deferOutstandingGuides(runtime);
|
|
354319
354340
|
const checkpointed = this.checkpoint(runtime.target).checkpointed;
|
|
354320
354341
|
runtime.stopCheckpointed = checkpointed;
|
|
354321
|
-
runtime.runner.abortActiveKernelRun();
|
|
354342
|
+
runtime.runner.abortActiveKernelRun("user_stop");
|
|
354322
354343
|
runtime.runner.emitWorkEvent({
|
|
354323
354344
|
type: "status",
|
|
354324
354345
|
content: "Stop requested. Saving progress and interrupting this conversation.",
|
|
@@ -354452,6 +354473,7 @@ ${item.goalObjective}` : item.text,
|
|
|
354452
354473
|
async run(runtime, message, options) {
|
|
354453
354474
|
this.applyOptions(runtime.runner, options);
|
|
354454
354475
|
let lastTokens = await this.runSingle(runtime, message);
|
|
354476
|
+
this.rememberAutomaticAssistantFingerprint(runtime, message);
|
|
354455
354477
|
if (runtime.stopRequestedRunId === runtime.runId) {
|
|
354456
354478
|
this.mirrorHostIfTargetActive(runtime);
|
|
354457
354479
|
return this.result(runtime, lastTokens);
|
|
@@ -354478,6 +354500,7 @@ ${item.goalObjective}` : item.text,
|
|
|
354478
354500
|
}
|
|
354479
354501
|
if (batchGuides.length === 1) {
|
|
354480
354502
|
lastTokens = await this.runSingle(runtime, next.message, next.queueMode);
|
|
354503
|
+
if (this.repeatedAutomaticAssistant(runtime, next.message, next.queueMode)) break;
|
|
354481
354504
|
continue;
|
|
354482
354505
|
}
|
|
354483
354506
|
const batchText = batchGuides.map((guide, index) => `Guide ${index + 1}: ${guide.text}`).join("\n");
|
|
@@ -354488,8 +354511,11 @@ ${batchText}`,
|
|
|
354488
354511
|
batchGuides
|
|
354489
354512
|
};
|
|
354490
354513
|
lastTokens = await this.runSingle(runtime, batchMessage, "steer");
|
|
354514
|
+
if (this.repeatedAutomaticAssistant(runtime, batchMessage, "steer")) break;
|
|
354491
354515
|
} else {
|
|
354492
|
-
|
|
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;
|
|
354493
354519
|
}
|
|
354494
354520
|
}
|
|
354495
354521
|
const rootMessage = runtime.runner.subagents.readRootInbox()[0];
|
|
@@ -354524,6 +354550,35 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354524
354550
|
this.mirrorHostIfTargetActive(runtime);
|
|
354525
354551
|
return this.result(runtime, lastTokens);
|
|
354526
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
|
+
}
|
|
354527
354582
|
/**
|
|
354528
354583
|
* Apply a model selection recorded while a Build block was running. The
|
|
354529
354584
|
* in-flight block never switches mid-block; the switch takes effect the next
|
|
@@ -354563,7 +354618,16 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354563
354618
|
tokens = await Promise.race([
|
|
354564
354619
|
runtime.runner.process(message),
|
|
354565
354620
|
new Promise((_3, reject) => {
|
|
354566
|
-
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);
|
|
354567
354631
|
})
|
|
354568
354632
|
]);
|
|
354569
354633
|
} catch (error) {
|
|
@@ -354624,7 +354688,8 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354624
354688
|
guideReceipts: /* @__PURE__ */ new Map(),
|
|
354625
354689
|
guideEnvelopes: /* @__PURE__ */ new Map(),
|
|
354626
354690
|
goalContinuationTimer: void 0,
|
|
354627
|
-
pendingContinuationRunId: void 0
|
|
354691
|
+
pendingContinuationRunId: void 0,
|
|
354692
|
+
lastAutomaticAssistantFingerprint: void 0
|
|
354628
354693
|
};
|
|
354629
354694
|
runner.setGoalContinuationGate(() => {
|
|
354630
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": {
|