newmark-agent 0.3.11 → 0.3.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/config.example.json +6 -0
- package/dist/cli-commands.d.ts +7 -0
- package/dist/cli-commands.js +206 -15
- package/dist/cli-discovery.d.ts +15 -0
- package/dist/cli-discovery.js +182 -0
- package/dist/cli-help.d.ts +2 -0
- package/dist/cli-help.js +25 -1
- package/dist/conversation-utility-host.bundle.cjs +357 -84
- package/dist/core/agent.d.ts +18 -3
- package/dist/core/agent.js +214 -30
- package/dist/core/agentKernelRunner.js +53 -8
- package/dist/core/config.d.ts +7 -2
- package/dist/core/config.js +24 -6
- package/dist/core/conversationKernel.js +1 -1
- package/dist/core/electronUtilityRuntimePool.d.ts +11 -0
- package/dist/core/electronUtilityRuntimePool.js +62 -0
- package/dist/core/flow-runner.js +1 -1
- package/dist/core/modelValidationStore.d.ts +4 -1
- package/dist/core/modelValidationStore.js +7 -1
- package/dist/core/workspace.d.ts +6 -0
- package/dist/core/workspace.js +14 -0
- package/dist/core/wslAgentRuntimePool.d.ts +4 -0
- package/dist/core/wslAgentRuntimePool.js +56 -0
- package/dist/launcher.js +40 -11
- package/dist/llm/provider.d.ts +8 -5
- package/dist/llm/provider.js +85 -33
- package/dist/main.js +167 -45
- package/dist/preload.js +6 -0
- package/dist/providers/chat-completions.adapter.js +1 -5
- package/dist/providers/provider-events.d.ts +7 -0
- package/dist/providers/provider-events.js +44 -0
- package/dist/providers/responses.adapter.js +1 -3
- package/dist/tui/src/adapters/core-runtime-adapter.js +40 -3
- package/dist/tui/src/app.js +47 -13
- package/dist/tui/src/render.js +23 -7
- package/dist/tui/src/state.js +61 -9
- package/dist/ui/index.html +250 -84
- package/dist/wsl-agent-host.bundle.cjs +357 -84
- package/package.json +14 -5
|
@@ -326959,7 +326959,12 @@ async function runFlowBuild(agent, prompt, options) {
|
|
|
326959
326959
|
if (!options.signal?.aborted && typeof agent.emitWorkEvent === "function") {
|
|
326960
326960
|
agent.emitWorkEvent({ type: "error", content: reportedError.message, runId });
|
|
326961
326961
|
}
|
|
326962
|
-
agent.finishConversationWorkRun(
|
|
326962
|
+
agent.finishConversationWorkRun(
|
|
326963
|
+
runId,
|
|
326964
|
+
options.signal?.aborted ? "interrupted" : "error",
|
|
326965
|
+
void 0,
|
|
326966
|
+
options.signal?.aborted ? "" : reportedError.message
|
|
326967
|
+
);
|
|
326963
326968
|
agent.flushWorkspaceConversationState();
|
|
326964
326969
|
}
|
|
326965
326970
|
throw reportedError;
|
|
@@ -327619,8 +327624,10 @@ var ConfigManager = class {
|
|
|
327619
327624
|
rootPath;
|
|
327620
327625
|
config;
|
|
327621
327626
|
workspaceOverrides;
|
|
327622
|
-
|
|
327627
|
+
readOnly;
|
|
327628
|
+
constructor(rootPath, options = {}) {
|
|
327623
327629
|
this.rootPath = rootPath;
|
|
327630
|
+
this.readOnly = options.readOnly === true;
|
|
327624
327631
|
this.workspaceOverrides = /* @__PURE__ */ new Map();
|
|
327625
327632
|
this.config = this.load();
|
|
327626
327633
|
}
|
|
@@ -327635,17 +327642,19 @@ var ConfigManager = class {
|
|
|
327635
327642
|
const raw = JSON.parse(readJsonText(cp));
|
|
327636
327643
|
const normalized = normalizeConfigShape(raw, true);
|
|
327637
327644
|
if (isCorruptConfig(raw, normalized)) {
|
|
327645
|
+
if (this.readOnly) return defaultConfig();
|
|
327638
327646
|
this.backupConfig(cp, "invalid-shape");
|
|
327639
327647
|
return this.writeRecoveredConfig(cp);
|
|
327640
327648
|
}
|
|
327641
327649
|
if (migrateProviderIdsInConfig(normalized)) {
|
|
327642
327650
|
try {
|
|
327643
|
-
fs3.writeFileSync(cp, JSON.stringify(normalized, null, 2), "utf-8");
|
|
327651
|
+
if (!this.readOnly) fs3.writeFileSync(cp, JSON.stringify(normalized, null, 2), "utf-8");
|
|
327644
327652
|
} catch {
|
|
327645
327653
|
}
|
|
327646
327654
|
}
|
|
327647
327655
|
return normalized;
|
|
327648
327656
|
} catch {
|
|
327657
|
+
if (this.readOnly) return defaultConfig();
|
|
327649
327658
|
this.backupConfig(cp, "invalid-json");
|
|
327650
327659
|
return this.writeRecoveredConfig(cp);
|
|
327651
327660
|
}
|
|
@@ -327691,10 +327700,12 @@ var ConfigManager = class {
|
|
|
327691
327700
|
this.config[section][key3] = { value: normalizedValue };
|
|
327692
327701
|
}
|
|
327693
327702
|
save() {
|
|
327703
|
+
if (this.readOnly) return;
|
|
327694
327704
|
const j2 = JSON.stringify(this.config, null, 2);
|
|
327695
327705
|
fs3.writeFileSync(path3.join(this.rootPath, "config.json"), j2, "utf-8");
|
|
327696
327706
|
}
|
|
327697
327707
|
saveTo(targetPath) {
|
|
327708
|
+
if (this.readOnly) return;
|
|
327698
327709
|
const j2 = JSON.stringify(this.config, null, 2);
|
|
327699
327710
|
fs3.writeFileSync(targetPath, j2, "utf-8");
|
|
327700
327711
|
}
|
|
@@ -327919,12 +327930,14 @@ var ConfigManager = class {
|
|
|
327919
327930
|
return providers;
|
|
327920
327931
|
}
|
|
327921
327932
|
writeRecoveredConfig(configPath) {
|
|
327933
|
+
if (this.readOnly) return defaultConfig();
|
|
327922
327934
|
const config = loadExampleConfig();
|
|
327923
327935
|
fs3.mkdirSync(path3.dirname(configPath), { recursive: true });
|
|
327924
327936
|
fs3.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
327925
327937
|
return config;
|
|
327926
327938
|
}
|
|
327927
327939
|
backupConfig(configPath, reason) {
|
|
327940
|
+
if (this.readOnly) return;
|
|
327928
327941
|
try {
|
|
327929
327942
|
if (!fs3.existsSync(configPath)) return;
|
|
327930
327943
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -328269,7 +328282,10 @@ function defaultConfig() {
|
|
|
328269
328282
|
general: {
|
|
328270
328283
|
tone: { _description: "Conversation style", _type: "choice", _values: ["strict_simple", "casual_friendly"], value: "strict_simple" },
|
|
328271
328284
|
language: { _description: "Default language", _type: "choice", _values: ["en", "zh", "auto"], value: "auto" },
|
|
328272
|
-
|
|
328285
|
+
// A first-run desktop window must have a deterministic close/exit
|
|
328286
|
+
// contract. Users who explicitly choose minimize-to-tray keep that
|
|
328287
|
+
// choice, but a fresh install must not hide the process on OS close.
|
|
328288
|
+
close_behavior: { _description: "Close behavior", _type: "choice", _values: ["minimize", "exit"], value: "exit" },
|
|
328273
328289
|
default_input: { _description: "Default input mode", _type: "choice", _values: ["guide", "next"], value: "guide" },
|
|
328274
328290
|
auto_archive_on_close: { _description: "Auto archive on close", _type: "boolean", value: true }
|
|
328275
328291
|
},
|
|
@@ -328608,6 +328624,38 @@ function providerAbortError(signal) {
|
|
|
328608
328624
|
if (!error.name || error.name === "Error") error.name = "AbortError";
|
|
328609
328625
|
return error;
|
|
328610
328626
|
}
|
|
328627
|
+
function providerStreamTimeoutError(timeoutMs) {
|
|
328628
|
+
const error = new Error("Stream read timeout");
|
|
328629
|
+
error.name = "TimeoutError";
|
|
328630
|
+
error.message = `Stream read timeout after ${timeoutMs}ms`;
|
|
328631
|
+
return error;
|
|
328632
|
+
}
|
|
328633
|
+
async function readProviderStreamChunk(reader, signal, timeoutMs = 3e4) {
|
|
328634
|
+
if (signal.aborted) throw providerAbortError(signal);
|
|
328635
|
+
let timer;
|
|
328636
|
+
let onAbort;
|
|
328637
|
+
const abortPromise = new Promise((_3, reject) => {
|
|
328638
|
+
onAbort = () => reject(providerAbortError(signal));
|
|
328639
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
328640
|
+
});
|
|
328641
|
+
const timeoutPromise = new Promise((_3, reject) => {
|
|
328642
|
+
timer = setTimeout(() => reject(providerStreamTimeoutError(timeoutMs)), timeoutMs);
|
|
328643
|
+
});
|
|
328644
|
+
try {
|
|
328645
|
+
return await Promise.race([reader.read(), abortPromise, timeoutPromise]);
|
|
328646
|
+
} catch (error) {
|
|
328647
|
+
if (signal.aborted || error instanceof Error && error.name === "TimeoutError") {
|
|
328648
|
+
try {
|
|
328649
|
+
await reader.cancel(error);
|
|
328650
|
+
} catch {
|
|
328651
|
+
}
|
|
328652
|
+
}
|
|
328653
|
+
throw error;
|
|
328654
|
+
} finally {
|
|
328655
|
+
if (timer) clearTimeout(timer);
|
|
328656
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
328657
|
+
}
|
|
328658
|
+
}
|
|
328611
328659
|
function parseProviderSse(raw) {
|
|
328612
328660
|
const events = [];
|
|
328613
328661
|
for (const block of String(raw || "").replace(/\r\n/g, "\n").split(/\n\n+/)) {
|
|
@@ -328843,12 +328891,7 @@ var ChatCompletionsAdapter = class {
|
|
|
328843
328891
|
let emittedTool = false;
|
|
328844
328892
|
try {
|
|
328845
328893
|
while (true) {
|
|
328846
|
-
|
|
328847
|
-
const readPromise = reader.read();
|
|
328848
|
-
const timeoutPromise = new Promise(
|
|
328849
|
-
(_3, reject) => setTimeout(() => reject(new Error("Stream read timeout")), 3e4)
|
|
328850
|
-
);
|
|
328851
|
-
const { done, value } = await Promise.race([readPromise, timeoutPromise]);
|
|
328894
|
+
const { done, value } = await readProviderStreamChunk(reader, signal);
|
|
328852
328895
|
if (done) break;
|
|
328853
328896
|
buffer += decoder.decode(value, { stream: true });
|
|
328854
328897
|
const lines = buffer.split("\n");
|
|
@@ -329086,8 +329129,7 @@ var ResponsesAdapter = class {
|
|
|
329086
329129
|
let streamError = "";
|
|
329087
329130
|
try {
|
|
329088
329131
|
while (true) {
|
|
329089
|
-
|
|
329090
|
-
const { done, value } = await reader.read();
|
|
329132
|
+
const { done, value } = await readProviderStreamChunk(reader, signal);
|
|
329091
329133
|
if (done) break;
|
|
329092
329134
|
buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
|
|
329093
329135
|
const blocks = buffer.split(/\n\n+/);
|
|
@@ -329297,6 +329339,16 @@ function createProviderAdapter(providerId, apiMode) {
|
|
|
329297
329339
|
}
|
|
329298
329340
|
|
|
329299
329341
|
// src/llm/provider.ts
|
|
329342
|
+
var DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS = 9e4;
|
|
329343
|
+
var MIN_PROVIDER_REQUEST_TIMEOUT_MS = 50;
|
|
329344
|
+
function providerTimeoutError(timeoutMs) {
|
|
329345
|
+
const error = new Error(`Provider request timed out after ${timeoutMs}ms`);
|
|
329346
|
+
error.name = "TimeoutError";
|
|
329347
|
+
return error;
|
|
329348
|
+
}
|
|
329349
|
+
function isProviderTimeoutError(error) {
|
|
329350
|
+
return error instanceof Error && error.name === "TimeoutError";
|
|
329351
|
+
}
|
|
329300
329352
|
function abortFailure(signal) {
|
|
329301
329353
|
const reason = signal?.reason;
|
|
329302
329354
|
const error = reason instanceof Error ? reason : new Error(reason ? String(reason) : "LLM request aborted");
|
|
@@ -329326,13 +329378,14 @@ function parseProviderSse2(raw) {
|
|
|
329326
329378
|
return events;
|
|
329327
329379
|
}
|
|
329328
329380
|
var LLMProvider = class _LLMProvider {
|
|
329329
|
-
constructor(name50, baseUrl, apiKey, explicitProtocol, openAIMode = "chat_stream", useProviderAdaptersV2 = false) {
|
|
329381
|
+
constructor(name50, baseUrl, apiKey, explicitProtocol, openAIMode = "chat_stream", useProviderAdaptersV2 = false, requestTimeoutMs = DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS) {
|
|
329330
329382
|
this.name = name50;
|
|
329331
329383
|
this.baseUrl = baseUrl;
|
|
329332
329384
|
this.apiKey = apiKey;
|
|
329333
329385
|
this.explicitProtocol = explicitProtocol;
|
|
329334
329386
|
this.openAIMode = openAIMode;
|
|
329335
329387
|
this.useProviderAdaptersV2 = useProviderAdaptersV2;
|
|
329388
|
+
this.requestTimeoutMs = requestTimeoutMs;
|
|
329336
329389
|
}
|
|
329337
329390
|
name;
|
|
329338
329391
|
baseUrl;
|
|
@@ -329340,8 +329393,25 @@ var LLMProvider = class _LLMProvider {
|
|
|
329340
329393
|
explicitProtocol;
|
|
329341
329394
|
openAIMode;
|
|
329342
329395
|
useProviderAdaptersV2;
|
|
329396
|
+
requestTimeoutMs;
|
|
329343
329397
|
static nodeHttpTransport = null;
|
|
329344
329398
|
static powershellTransport = null;
|
|
329399
|
+
effectiveRequestTimeout(timeoutMs) {
|
|
329400
|
+
const requested = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS;
|
|
329401
|
+
const configured = Number.isFinite(this.requestTimeoutMs) && this.requestTimeoutMs > 0 ? this.requestTimeoutMs : DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS;
|
|
329402
|
+
return Math.max(MIN_PROVIDER_REQUEST_TIMEOUT_MS, Math.min(requested, configured));
|
|
329403
|
+
}
|
|
329404
|
+
async withRequestTimeout(promise, timeoutMs, signal) {
|
|
329405
|
+
let timer;
|
|
329406
|
+
const timeoutPromise = new Promise((_3, reject) => {
|
|
329407
|
+
timer = setTimeout(() => reject(providerTimeoutError(timeoutMs)), timeoutMs);
|
|
329408
|
+
});
|
|
329409
|
+
try {
|
|
329410
|
+
return await abortable(Promise.race([promise, timeoutPromise]), signal);
|
|
329411
|
+
} finally {
|
|
329412
|
+
if (timer) clearTimeout(timer);
|
|
329413
|
+
}
|
|
329414
|
+
}
|
|
329345
329415
|
intelligenceConfig(tier) {
|
|
329346
329416
|
switch (tier) {
|
|
329347
329417
|
case "low":
|
|
@@ -329443,6 +329513,7 @@ var LLMProvider = class _LLMProvider {
|
|
|
329443
329513
|
};
|
|
329444
329514
|
}
|
|
329445
329515
|
async postJsonWithFetchFallback(url, headers, body, timeoutMs = 12e4, signal) {
|
|
329516
|
+
const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
|
|
329446
329517
|
if (this.isPlainHttpLoopback(url)) {
|
|
329447
329518
|
const pathname = (() => {
|
|
329448
329519
|
try {
|
|
@@ -329452,7 +329523,7 @@ var LLMProvider = class _LLMProvider {
|
|
|
329452
329523
|
}
|
|
329453
329524
|
})();
|
|
329454
329525
|
this.transportDiagnostic("loopback:start", pathname);
|
|
329455
|
-
const local = await this.nodeHttpJson("POST", url, headers, JSON.stringify(body), signal);
|
|
329526
|
+
const local = await this.nodeHttpJson("POST", url, headers, JSON.stringify(body), signal, effectiveTimeout);
|
|
329456
329527
|
this.transportDiagnostic("loopback:complete", `status=${local.status} bytes=${Buffer.byteLength(local.body || "")}`);
|
|
329457
329528
|
return {
|
|
329458
329529
|
ok: local.status >= 200 && local.status < 300,
|
|
@@ -329466,7 +329537,7 @@ var LLMProvider = class _LLMProvider {
|
|
|
329466
329537
|
const forwardAbort = () => abort.abort(signal?.reason);
|
|
329467
329538
|
if (signal?.aborted) forwardAbort();
|
|
329468
329539
|
else signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
329469
|
-
const timer = setTimeout(() => abort.abort(),
|
|
329540
|
+
const timer = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
|
|
329470
329541
|
try {
|
|
329471
329542
|
const response = await fetch(url, {
|
|
329472
329543
|
method: "POST",
|
|
@@ -329477,8 +329548,9 @@ var LLMProvider = class _LLMProvider {
|
|
|
329477
329548
|
return response;
|
|
329478
329549
|
} catch (e3) {
|
|
329479
329550
|
if (signal?.aborted) throw abortFailure(signal);
|
|
329551
|
+
if (abort.signal.aborted) throw abortFailure(abort.signal);
|
|
329480
329552
|
if (!this.shouldUseNodeHttpFallback(e3)) throw e3;
|
|
329481
|
-
const fallback = await this.nodeHttpJson("POST", url, headers, JSON.stringify(body), signal);
|
|
329553
|
+
const fallback = await this.nodeHttpJson("POST", url, headers, JSON.stringify(body), signal, effectiveTimeout);
|
|
329482
329554
|
return {
|
|
329483
329555
|
ok: fallback.status >= 200 && fallback.status < 300,
|
|
329484
329556
|
status: fallback.status,
|
|
@@ -329492,14 +329564,16 @@ var LLMProvider = class _LLMProvider {
|
|
|
329492
329564
|
}
|
|
329493
329565
|
}
|
|
329494
329566
|
async getJsonWithFetchFallback(url, headers, timeoutMs = 3e4) {
|
|
329567
|
+
const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
|
|
329495
329568
|
const abort = new AbortController();
|
|
329496
|
-
const timer = setTimeout(() => abort.abort(),
|
|
329569
|
+
const timer = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
|
|
329497
329570
|
try {
|
|
329498
329571
|
const response = await fetch(url, { method: "GET", headers, signal: abort.signal });
|
|
329499
329572
|
return response;
|
|
329500
329573
|
} catch (e3) {
|
|
329574
|
+
if (abort.signal.aborted) throw abortFailure(abort.signal);
|
|
329501
329575
|
if (!this.shouldUseNodeHttpFallback(e3)) throw e3;
|
|
329502
|
-
const fallback = await this.nodeHttpJson("GET", url, headers);
|
|
329576
|
+
const fallback = await this.nodeHttpJson("GET", url, headers, "", void 0, effectiveTimeout);
|
|
329503
329577
|
return {
|
|
329504
329578
|
ok: fallback.status >= 200 && fallback.status < 300,
|
|
329505
329579
|
status: fallback.status,
|
|
@@ -329512,14 +329586,16 @@ var LLMProvider = class _LLMProvider {
|
|
|
329512
329586
|
}
|
|
329513
329587
|
}
|
|
329514
329588
|
shouldUseNodeHttpFallback(error) {
|
|
329515
|
-
return error instanceof TypeError && /fetch failed/i.test(error.message)
|
|
329589
|
+
return error instanceof TypeError && /fetch failed/i.test(error.message);
|
|
329516
329590
|
}
|
|
329517
|
-
nodeHttpJson(method, urlValue, headers, body = "", signal) {
|
|
329591
|
+
nodeHttpJson(method, urlValue, headers, body = "", signal, timeoutMs = DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS) {
|
|
329592
|
+
const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
|
|
329518
329593
|
if (_LLMProvider.nodeHttpTransport) {
|
|
329519
|
-
return
|
|
329594
|
+
return this.withRequestTimeout(_LLMProvider.nodeHttpTransport(method, urlValue, headers, body), effectiveTimeout, signal).catch((error) => {
|
|
329520
329595
|
if (signal?.aborted) throw abortFailure(signal);
|
|
329596
|
+
if (isProviderTimeoutError(error)) throw error;
|
|
329521
329597
|
if (process.platform === "win32") {
|
|
329522
|
-
return this.powershellJson(method, urlValue, headers, body, signal);
|
|
329598
|
+
return this.powershellJson(method, urlValue, headers, body, signal, effectiveTimeout);
|
|
329523
329599
|
}
|
|
329524
329600
|
throw error;
|
|
329525
329601
|
});
|
|
@@ -329564,8 +329640,8 @@ var LLMProvider = class _LLMProvider {
|
|
|
329564
329640
|
else fail(new Error("Node HTTP response closed before completion"));
|
|
329565
329641
|
});
|
|
329566
329642
|
});
|
|
329567
|
-
req.setTimeout(
|
|
329568
|
-
req.destroy(
|
|
329643
|
+
req.setTimeout(effectiveTimeout, () => {
|
|
329644
|
+
req.destroy(providerTimeoutError(effectiveTimeout));
|
|
329569
329645
|
});
|
|
329570
329646
|
req.on("error", reject);
|
|
329571
329647
|
const onAbort = () => req.destroy(abortFailure(signal));
|
|
@@ -329576,15 +329652,17 @@ var LLMProvider = class _LLMProvider {
|
|
|
329576
329652
|
req.end();
|
|
329577
329653
|
}).catch((error) => {
|
|
329578
329654
|
if (signal?.aborted) throw abortFailure(signal);
|
|
329655
|
+
if (isProviderTimeoutError(error)) throw error;
|
|
329579
329656
|
if (process.platform === "win32") {
|
|
329580
|
-
return this.powershellJson(method, urlValue, headers, body, signal);
|
|
329657
|
+
return this.powershellJson(method, urlValue, headers, body, signal, effectiveTimeout);
|
|
329581
329658
|
}
|
|
329582
329659
|
throw error;
|
|
329583
329660
|
});
|
|
329584
329661
|
}
|
|
329585
|
-
powershellJson(method, urlValue, headers, body = "", signal) {
|
|
329662
|
+
powershellJson(method, urlValue, headers, body = "", signal, timeoutMs = DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS) {
|
|
329663
|
+
const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
|
|
329586
329664
|
if (_LLMProvider.powershellTransport) {
|
|
329587
|
-
return _LLMProvider.powershellTransport(method, urlValue, headers, body);
|
|
329665
|
+
return this.withRequestTimeout(_LLMProvider.powershellTransport(method, urlValue, headers, body), effectiveTimeout, signal);
|
|
329588
329666
|
}
|
|
329589
329667
|
return new Promise((resolve16, reject) => {
|
|
329590
329668
|
const headerJson = JSON.stringify(headers);
|
|
@@ -329613,7 +329691,7 @@ var LLMProvider = class _LLMProvider {
|
|
|
329613
329691
|
" $raw = $headerJson | ConvertFrom-Json",
|
|
329614
329692
|
" foreach ($p in $raw.PSObject.Properties) { $headers[$p.Name] = [string]$p.Value }",
|
|
329615
329693
|
"}",
|
|
329616
|
-
|
|
329694
|
+
`'$params = @{ Uri = $uri; Method = $method; Headers = $headers; UseBasicParsing = $true; TimeoutSec = ${Math.max(1, Math.ceil(effectiveTimeout / 1e3))} }`,
|
|
329617
329695
|
'if ($method -eq "POST") { $params["Body"] = $bodyJson }',
|
|
329618
329696
|
'if ($method -eq "POST") { $params["ContentType"] = "application/json; charset=utf-8" }',
|
|
329619
329697
|
"$resp = Invoke-WebRequest @params",
|
|
@@ -329645,8 +329723,8 @@ var LLMProvider = class _LLMProvider {
|
|
|
329645
329723
|
const timer = setTimeout(() => {
|
|
329646
329724
|
child.kill();
|
|
329647
329725
|
cleanup();
|
|
329648
|
-
reject(
|
|
329649
|
-
},
|
|
329726
|
+
reject(providerTimeoutError(effectiveTimeout));
|
|
329727
|
+
}, effectiveTimeout + 5e3);
|
|
329650
329728
|
child.stdout.setEncoding("utf8");
|
|
329651
329729
|
child.stderr.setEncoding("utf8");
|
|
329652
329730
|
child.stdout.on("data", (chunk) => {
|
|
@@ -330094,10 +330172,10 @@ ${responsePath}
|
|
|
330094
330172
|
return this.shouldUseResponsesFallback(Number(match[1]), errorText);
|
|
330095
330173
|
}
|
|
330096
330174
|
/**
|
|
330097
|
-
* Loopback-aware transport injected into adapter `execute`.
|
|
330098
|
-
*
|
|
330099
|
-
*
|
|
330100
|
-
*
|
|
330175
|
+
* Loopback-aware transport injected into adapter `execute`. Streaming
|
|
330176
|
+
* requests retain the fetch-to-node fallback for transport failures, while
|
|
330177
|
+
* a local deadline is returned directly so one request cannot become a
|
|
330178
|
+
* second Windows fallback request.
|
|
330101
330179
|
*/
|
|
330102
330180
|
buildProviderAdapterTransport() {
|
|
330103
330181
|
return async (request, signal) => {
|
|
@@ -330106,7 +330184,8 @@ ${responsePath}
|
|
|
330106
330184
|
const forwardAbort = () => abort.abort(signal?.reason);
|
|
330107
330185
|
if (signal?.aborted) forwardAbort();
|
|
330108
330186
|
else signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
330109
|
-
const
|
|
330187
|
+
const effectiveTimeout = this.effectiveRequestTimeout(12e4);
|
|
330188
|
+
const timer = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
|
|
330110
330189
|
try {
|
|
330111
330190
|
try {
|
|
330112
330191
|
return await fetch(request.url, {
|
|
@@ -330117,6 +330196,7 @@ ${responsePath}
|
|
|
330117
330196
|
});
|
|
330118
330197
|
} catch (error) {
|
|
330119
330198
|
if (signal?.aborted) throw abortFailure(signal);
|
|
330199
|
+
if (abort.signal.aborted) throw abortFailure(abort.signal);
|
|
330120
330200
|
if (!this.shouldUseNodeHttpFallback(error)) throw error;
|
|
330121
330201
|
const fallbackHeaders = { ...request.headers };
|
|
330122
330202
|
delete fallbackHeaders["Accept"];
|
|
@@ -330124,7 +330204,7 @@ ${responsePath}
|
|
|
330124
330204
|
request.url,
|
|
330125
330205
|
fallbackHeaders,
|
|
330126
330206
|
{ ...request.body, stream: false },
|
|
330127
|
-
|
|
330207
|
+
effectiveTimeout,
|
|
330128
330208
|
signal
|
|
330129
330209
|
);
|
|
330130
330210
|
return this.toTransportResponse(fallback);
|
|
@@ -330234,7 +330314,8 @@ ${responsePath}
|
|
|
330234
330314
|
const forwardAbort = () => abort.abort(signal?.reason);
|
|
330235
330315
|
if (signal?.aborted) forwardAbort();
|
|
330236
330316
|
else signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
330237
|
-
const
|
|
330317
|
+
const effectiveTimeout = this.effectiveRequestTimeout(12e4);
|
|
330318
|
+
const timeout = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
|
|
330238
330319
|
let reader = null;
|
|
330239
330320
|
try {
|
|
330240
330321
|
let response;
|
|
@@ -330246,9 +330327,10 @@ ${responsePath}
|
|
|
330246
330327
|
signal: abort.signal
|
|
330247
330328
|
});
|
|
330248
330329
|
} catch (e3) {
|
|
330330
|
+
if (signal?.aborted) throw abortFailure(signal);
|
|
330331
|
+
if (abort.signal.aborted) throw abortFailure(abort.signal);
|
|
330249
330332
|
if (!this.shouldUseNodeHttpFallback(e3)) throw e3;
|
|
330250
330333
|
clearTimeout(timeout);
|
|
330251
|
-
if (signal?.aborted) throw abortFailure(signal);
|
|
330252
330334
|
yield* this.githubModelsChatNonStreaming(url, body, signal);
|
|
330253
330335
|
return;
|
|
330254
330336
|
}
|
|
@@ -330268,13 +330350,9 @@ ${responsePath}
|
|
|
330268
330350
|
let currentReasoningContent = "";
|
|
330269
330351
|
let contentPolicyBlocked = false;
|
|
330270
330352
|
let emittedContent = false;
|
|
330353
|
+
const streamSignal = signal || new AbortController().signal;
|
|
330271
330354
|
while (true) {
|
|
330272
|
-
|
|
330273
|
-
const readPromise = reader.read();
|
|
330274
|
-
const timeoutPromise = new Promise(
|
|
330275
|
-
(_3, reject) => setTimeout(() => reject(new Error("Stream read timeout")), 3e4)
|
|
330276
|
-
);
|
|
330277
|
-
const { done, value } = await Promise.race([readPromise, timeoutPromise]);
|
|
330355
|
+
const { done, value } = await readProviderStreamChunk(reader, streamSignal);
|
|
330278
330356
|
if (done) break;
|
|
330279
330357
|
buffer += decoder.decode(value, { stream: true });
|
|
330280
330358
|
const lines = buffer.split("\n");
|
|
@@ -337920,6 +337998,19 @@ var WorkspaceManager = class {
|
|
|
337920
337998
|
this.saveState();
|
|
337921
337999
|
}
|
|
337922
338000
|
}
|
|
338001
|
+
/**
|
|
338002
|
+
* Re-read the registry and persisted current-workspace pointer after another
|
|
338003
|
+
* Newmark entrypoint updates Work/*.json. This intentionally does not create
|
|
338004
|
+
* a workspace: a refresh must reflect the shared on-disk state exactly.
|
|
338005
|
+
*/
|
|
338006
|
+
reloadFromStorage() {
|
|
338007
|
+
if (this.detached) return this.current;
|
|
338008
|
+
this.scan();
|
|
338009
|
+
this.validate();
|
|
338010
|
+
this.current = null;
|
|
338011
|
+
this.restoreCurrent();
|
|
338012
|
+
return this.current;
|
|
338013
|
+
}
|
|
337923
338014
|
saveInternal() {
|
|
337924
338015
|
if (this.detached) return;
|
|
337925
338016
|
const p = path16.join(this.rootPath, "Work", "Local.json");
|
|
@@ -340010,17 +340101,35 @@ function normalizePublicProviderError(error, secrets = []) {
|
|
|
340010
340101
|
}
|
|
340011
340102
|
return raw.slice(0, 1200);
|
|
340012
340103
|
}
|
|
340104
|
+
function throwIfKernelAborted(signal) {
|
|
340105
|
+
if (!signal?.aborted) return;
|
|
340106
|
+
const reason = signal.reason;
|
|
340107
|
+
if (reason instanceof Error) {
|
|
340108
|
+
reason.name = "AbortError";
|
|
340109
|
+
throw reason;
|
|
340110
|
+
}
|
|
340111
|
+
const error = new Error(reason ? String(reason) : "Agent run aborted");
|
|
340112
|
+
error.name = "AbortError";
|
|
340113
|
+
throw error;
|
|
340114
|
+
}
|
|
340013
340115
|
async function runAgentKernel(agent) {
|
|
340014
340116
|
const stopContextTimer = performanceTimer("context_prepare", { conversationId: agent.activeConversationId });
|
|
340117
|
+
const processSignal = agent.activeProcessSignal();
|
|
340118
|
+
if (processSignal?.aborted) {
|
|
340119
|
+
stopContextTimer();
|
|
340120
|
+
throwIfKernelAborted(processSignal);
|
|
340121
|
+
}
|
|
340015
340122
|
if (!agent.engineModel()) {
|
|
340123
|
+
const message = "No LLM configured. Add provider in Settings > Models.";
|
|
340016
340124
|
agent.status = "error";
|
|
340017
340125
|
agent.saveWorkspaceConversationState();
|
|
340018
|
-
|
|
340126
|
+
throw new Error(message);
|
|
340019
340127
|
}
|
|
340020
340128
|
const [{ Agent: NativeAgent }, KernelStreamCompat] = await Promise.all([
|
|
340021
340129
|
Promise.resolve().then(() => (init_agentKernel(), agentKernel_exports)),
|
|
340022
340130
|
Promise.resolve().then(() => (init_stream_types(), stream_types_exports))
|
|
340023
340131
|
]);
|
|
340132
|
+
throwIfKernelAborted(processSignal);
|
|
340024
340133
|
const toolProvisioning = new ToolProvisionSession([], []);
|
|
340025
340134
|
let activeToolSurfaceIdentity = "";
|
|
340026
340135
|
let activeToolSurfaceNotice = "";
|
|
@@ -340046,6 +340155,7 @@ async function runAgentKernel(agent) {
|
|
|
340046
340155
|
const initialToolSurface = refreshToolSurface(true);
|
|
340047
340156
|
const assembledContext = agent.assembleContextV2(initialToolSurface.systemPromptNotice);
|
|
340048
340157
|
const systemPrompt = assembledContext.text;
|
|
340158
|
+
throwIfKernelAborted(processSignal);
|
|
340049
340159
|
let providerRequestCount = 0;
|
|
340050
340160
|
let bootstrappedCompressionAt = agent.lastCompression?.at || "";
|
|
340051
340161
|
stopContextTimer();
|
|
@@ -340066,6 +340176,24 @@ async function runAgentKernel(agent) {
|
|
|
340066
340176
|
kernel2.state.tools = toKernelTools(agent, initialToolSurface.definitions, toolProvisioning);
|
|
340067
340177
|
kernel2.state.messages = toKernelMessages(agent);
|
|
340068
340178
|
agent.attachAgentKernelRuntime(kernel2);
|
|
340179
|
+
let detachProcessAbort = () => {
|
|
340180
|
+
};
|
|
340181
|
+
if (processSignal) {
|
|
340182
|
+
const abortKernel = () => kernel2.abort();
|
|
340183
|
+
if (processSignal.aborted) {
|
|
340184
|
+
kernel2.abort();
|
|
340185
|
+
} else {
|
|
340186
|
+
processSignal.addEventListener("abort", abortKernel, { once: true });
|
|
340187
|
+
detachProcessAbort = () => processSignal.removeEventListener("abort", abortKernel);
|
|
340188
|
+
}
|
|
340189
|
+
}
|
|
340190
|
+
try {
|
|
340191
|
+
throwIfKernelAborted(processSignal);
|
|
340192
|
+
} catch (error) {
|
|
340193
|
+
detachProcessAbort();
|
|
340194
|
+
agent.attachAgentKernelRuntime(null);
|
|
340195
|
+
throw error;
|
|
340196
|
+
}
|
|
340069
340197
|
const tokens = [];
|
|
340070
340198
|
const runOnce = async (promptMessages, appendPromptToAgentHistory) => {
|
|
340071
340199
|
let lastAssistant = null;
|
|
@@ -340169,6 +340297,7 @@ async function runAgentKernel(agent) {
|
|
|
340169
340297
|
else agent.pendingOptions = agent.pendingOptions.filter((question) => !isPlanExecutionQuestion(question));
|
|
340170
340298
|
}
|
|
340171
340299
|
} finally {
|
|
340300
|
+
detachProcessAbort();
|
|
340172
340301
|
agent.attachAgentKernelRuntime(null);
|
|
340173
340302
|
}
|
|
340174
340303
|
agent.status = "idle";
|
|
@@ -340335,20 +340464,18 @@ async function transformContext(agent, messages, signal) {
|
|
|
340335
340464
|
const provider = agent.engineModel();
|
|
340336
340465
|
if (!provider || !compressionModel) return messages;
|
|
340337
340466
|
const newmarkMessages = publicHistoryFromKernelMessages(messages);
|
|
340338
|
-
const beforeCompression = JSON.stringify(newmarkMessages);
|
|
340339
340467
|
const compressionAt = agent.lastCompression?.at || "";
|
|
340340
|
-
await agent.maybeCompress(newmarkMessages, provider, processSignal, compressionModel);
|
|
340468
|
+
let compressed = await agent.maybeCompress(newmarkMessages, provider, processSignal, compressionModel);
|
|
340341
340469
|
if (processSignal?.aborted) return messages;
|
|
340342
|
-
|
|
340343
|
-
|
|
340344
|
-
await agent.maybeCompress(newmarkMessages, null, processSignal, compressionModel, true);
|
|
340470
|
+
if (compressed && agent.estimateContextTokens(newmarkMessages) >= Math.floor(agent.contextWindow(compressionModel).maxTokens * 0.82)) {
|
|
340471
|
+
compressed = await agent.maybeCompress(newmarkMessages, null, processSignal, compressionModel, true) || compressed;
|
|
340345
340472
|
}
|
|
340346
340473
|
const windowMax = agent.contextWindow(compressionModel).maxTokens;
|
|
340347
340474
|
const conservativeTokens = agent.estimateContextTokens(newmarkMessages);
|
|
340348
340475
|
if (conservativeTokens >= Math.floor(windowMax * 0.9) && !processSignal?.aborted) {
|
|
340349
|
-
await agent.maybeCompress(newmarkMessages, null, processSignal, compressionModel, true);
|
|
340476
|
+
compressed = await agent.maybeCompress(newmarkMessages, null, processSignal, compressionModel, true) || compressed;
|
|
340350
340477
|
}
|
|
340351
|
-
if (
|
|
340478
|
+
if (!compressed) return messages;
|
|
340352
340479
|
const durableMessages = toKernelMessagesFromHistory(newmarkMessages, agent);
|
|
340353
340480
|
if (agent.lastCompression?.at && agent.lastCompression.at !== compressionAt) {
|
|
340354
340481
|
agent.recordContextCompressionStep();
|
|
@@ -342239,9 +342366,11 @@ function key2(model) {
|
|
|
342239
342366
|
}
|
|
342240
342367
|
var FileModelValidationCache = class {
|
|
342241
342368
|
filePath;
|
|
342369
|
+
readOnly;
|
|
342242
342370
|
records = /* @__PURE__ */ new Map();
|
|
342243
|
-
constructor(rootPath) {
|
|
342371
|
+
constructor(rootPath, options = {}) {
|
|
342244
342372
|
this.filePath = path20.join(rootPath, "model-validation", "records.json");
|
|
342373
|
+
this.readOnly = options.readOnly === true;
|
|
342245
342374
|
this.load();
|
|
342246
342375
|
}
|
|
342247
342376
|
get(modelKey2) {
|
|
@@ -342250,10 +342379,12 @@ var FileModelValidationCache = class {
|
|
|
342250
342379
|
}
|
|
342251
342380
|
set(record) {
|
|
342252
342381
|
this.records.set(record.modelKey || key2(record.model), JSON.parse(JSON.stringify(record)));
|
|
342382
|
+
if (this.readOnly) return;
|
|
342253
342383
|
this.save();
|
|
342254
342384
|
}
|
|
342255
342385
|
delete(modelKey2) {
|
|
342256
342386
|
if (!this.records.delete(modelKey2)) return;
|
|
342387
|
+
if (this.readOnly) return;
|
|
342257
342388
|
this.save();
|
|
342258
342389
|
}
|
|
342259
342390
|
load() {
|
|
@@ -344129,7 +344260,7 @@ var Agent4 = class _Agent {
|
|
|
344129
344260
|
this.subagentName = options.subagentName || "";
|
|
344130
344261
|
this.subagentPrompt = options.subagentPrompt || "";
|
|
344131
344262
|
this.linkedPlanAccess = options.linkedPlanAccess;
|
|
344132
|
-
this.config = new ConfigManager(rootPath);
|
|
344263
|
+
this.config = new ConfigManager(rootPath, { readOnly: options.readOnlyConfig === true });
|
|
344133
344264
|
this.compressionHistoryArchive = new CompressionHistoryArchive(rootPath);
|
|
344134
344265
|
this.contextV2 = new AgentContextManager(rootPath, this.config);
|
|
344135
344266
|
this.agentRunService = this.config.contextFlag("agent_runtime_v2") ? new AgentRunService(path28.join(rootPath, ".newmark-context-v2")) : null;
|
|
@@ -344660,7 +344791,10 @@ var Agent4 = class _Agent {
|
|
|
344660
344791
|
}
|
|
344661
344792
|
const previousAuto = this.model === "auto" ? this.resolvedDeployment : null;
|
|
344662
344793
|
const qualified = parseDeploymentSelectionValue2(requested);
|
|
344663
|
-
const
|
|
344794
|
+
const legacyQualified = requested.includes("/") ? this.config.allModels().filter(
|
|
344795
|
+
(model2) => `${model2.provider_id}/${model2.name}` === requested || `${model2.provider}/${model2.name}` === requested
|
|
344796
|
+
) : [];
|
|
344797
|
+
const current = qualified ? this.config.findDeployment(qualified) : legacyQualified.length === 1 ? legacyQualified[0] : requested ? this.config.findModel(requested) : void 0;
|
|
344664
344798
|
this.model = current?.name || requested;
|
|
344665
344799
|
this.fixedDeployment = current ? this.deploymentRef(current) : qualified;
|
|
344666
344800
|
this.resolvedDeployment = null;
|
|
@@ -345777,7 +345911,7 @@ ${String(event.toolArgs || "")}`;
|
|
|
345777
345911
|
this.saveWorkspaceConversationState(true);
|
|
345778
345912
|
return true;
|
|
345779
345913
|
}
|
|
345780
|
-
finishConversationWorkRun(runId, status, endedAt = this.nowIso()) {
|
|
345914
|
+
finishConversationWorkRun(runId, status, endedAt = this.nowIso(), errorMessage = "") {
|
|
345781
345915
|
const run = this.workRuns.find((item) => item.runId === String(runId || ""));
|
|
345782
345916
|
if (!run) return false;
|
|
345783
345917
|
this.syncAgentRunTerminal(run.runId, status, endedAt);
|
|
@@ -345821,7 +345955,7 @@ ${String(event.toolArgs || "")}`;
|
|
|
345821
345955
|
this.enforceGoalTerminalInvariant(status, goalAudit);
|
|
345822
345956
|
this.emitWorkEvent({
|
|
345823
345957
|
type: status === "completed" ? "done" : status === "error" ? "error" : "status",
|
|
345824
|
-
content: status === "force_interrupted" ? "Force interrupted." : status === "interrupted" ? "Interrupted." : "Response complete.",
|
|
345958
|
+
content: status === "error" ? String(errorMessage || "").trim() || "Agent run failed." : status === "force_interrupted" ? "Force interrupted." : status === "interrupted" ? "Interrupted." : "Response complete.",
|
|
345825
345959
|
status,
|
|
345826
345960
|
runId: run.runId,
|
|
345827
345961
|
conversationId: run.target.conversationId,
|
|
@@ -345966,6 +346100,7 @@ ${String(event.toolArgs || "")}`;
|
|
|
345966
346100
|
this.activeAgentKernelRuntime = runtime;
|
|
345967
346101
|
this.awaitingAgentKernelRuntime = false;
|
|
345968
346102
|
if (!runtime) return;
|
|
346103
|
+
if (this.activeProcessAbortController?.signal.aborted) runtime.abort?.();
|
|
345969
346104
|
const queued = this.pendingAgentKernelQueue.splice(0);
|
|
345970
346105
|
for (const item of queued) {
|
|
345971
346106
|
const accepted = this.forwardAgentKernelQueueMessage(item.content, item.queueMode, item.clientMessageId, item.runId, item.images, item.hiddenUserInput);
|
|
@@ -347127,6 +347262,25 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347127
347262
|
this.loadWorkspaceConversationState();
|
|
347128
347263
|
return selected;
|
|
347129
347264
|
}
|
|
347265
|
+
refreshWorkspaceRegistryFromStorage() {
|
|
347266
|
+
const before = JSON.stringify({
|
|
347267
|
+
internal: this.workspace.internal,
|
|
347268
|
+
external: this.workspace.external,
|
|
347269
|
+
current: this.workspace.current
|
|
347270
|
+
});
|
|
347271
|
+
const selected = this.workspace.reloadFromStorage();
|
|
347272
|
+
const after = JSON.stringify({
|
|
347273
|
+
internal: this.workspace.internal,
|
|
347274
|
+
external: this.workspace.external,
|
|
347275
|
+
current: this.workspace.current
|
|
347276
|
+
});
|
|
347277
|
+
if (before === after) return selected;
|
|
347278
|
+
if (selected) this.config.loadWorkspaceConfig(selected.path);
|
|
347279
|
+
else this.config.clearWorkspaceOverrides();
|
|
347280
|
+
this.workspaceConversations.clear();
|
|
347281
|
+
this.loadWorkspaceConversationState();
|
|
347282
|
+
return selected;
|
|
347283
|
+
}
|
|
347130
347284
|
setConversation(id) {
|
|
347131
347285
|
const clean = this.safeConversationId(id || "default");
|
|
347132
347286
|
if (this.workspaceConversationKey() === this.loadedWorkspaceConversationKey) {
|
|
@@ -348120,28 +348274,25 @@ ${summary}`, segment, "local-summarize", true);
|
|
|
348120
348274
|
this.saveWorkspaceConversationState(true);
|
|
348121
348275
|
return { text, hiddenUserInput: true, goalContinuation: true };
|
|
348122
348276
|
}
|
|
348123
|
-
|
|
348277
|
+
buildSessionArchive(messages, mode, model, archiveDir) {
|
|
348124
348278
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace("T", "_").replace("Z", "");
|
|
348125
|
-
const
|
|
348126
|
-
|
|
348127
|
-
const filename = `session_${stamp}.md`;
|
|
348128
|
-
const outPath = path28.join(archiveDir, filename);
|
|
348129
|
-
let md = `# Newmark Session \u2014 ${stamp}
|
|
348279
|
+
const filename = `session_${stamp}_${crypto14.randomUUID().slice(0, 8)}.md`;
|
|
348280
|
+
let markdown = `# Newmark Session \u2014 ${stamp}
|
|
348130
348281
|
|
|
348131
348282
|
`;
|
|
348132
|
-
|
|
348283
|
+
markdown += `**Mode**: ${mode}
|
|
348133
348284
|
**Model**: ${model}
|
|
348134
348285
|
`;
|
|
348135
|
-
|
|
348286
|
+
markdown += `**Messages**: ${messages.length}
|
|
348136
348287
|
|
|
348137
348288
|
---
|
|
348138
348289
|
|
|
348139
348290
|
`;
|
|
348140
|
-
if (this.goal)
|
|
348291
|
+
if (this.goal) markdown += `**Goal**: ${this.goal.objective}
|
|
348141
348292
|
|
|
348142
348293
|
`;
|
|
348143
348294
|
for (const msg of messages) {
|
|
348144
|
-
|
|
348295
|
+
markdown += `**[${msg.role}] ${msg.timestamp}**
|
|
348145
348296
|
|
|
348146
348297
|
${msg.content}
|
|
348147
348298
|
|
|
@@ -348150,13 +348301,35 @@ ${msg.content}
|
|
|
348150
348301
|
const archived = archiveConversationImageAttachment(this.rootPath, archiveDir, attachment);
|
|
348151
348302
|
if (!archived) continue;
|
|
348152
348303
|
const alt = archived.name.replace(/[\]\r\n]/g, " ").trim() || "Submitted image";
|
|
348153
|
-
|
|
348304
|
+
markdown += `
|
|
348154
348305
|
|
|
348155
348306
|
`;
|
|
348156
348307
|
}
|
|
348157
348308
|
}
|
|
348158
|
-
|
|
348159
|
-
|
|
348309
|
+
return { filename, markdown };
|
|
348310
|
+
}
|
|
348311
|
+
writeSessionArchive(messages, mode, model) {
|
|
348312
|
+
const archiveDir = this.archiveDir();
|
|
348313
|
+
fs25.mkdirSync(archiveDir, { recursive: true });
|
|
348314
|
+
const archive = this.buildSessionArchive(messages, mode, model, archiveDir);
|
|
348315
|
+
fs25.writeFileSync(path28.join(archiveDir, archive.filename), archive.markdown, "utf-8");
|
|
348316
|
+
return archive.filename;
|
|
348317
|
+
}
|
|
348318
|
+
async writeSessionArchiveAsync(messages, mode, model, archiveDir = this.archiveDir()) {
|
|
348319
|
+
const archive = this.buildSessionArchive(messages, mode, model, archiveDir);
|
|
348320
|
+
await fs25.promises.mkdir(archiveDir, { recursive: true });
|
|
348321
|
+
const outPath = path28.join(archiveDir, archive.filename);
|
|
348322
|
+
const tempPath = `${outPath}.${process.pid}.${crypto14.randomUUID()}.tmp`;
|
|
348323
|
+
try {
|
|
348324
|
+
await fs25.promises.writeFile(tempPath, archive.markdown, "utf-8");
|
|
348325
|
+
await fs25.promises.rename(tempPath, outPath);
|
|
348326
|
+
} finally {
|
|
348327
|
+
try {
|
|
348328
|
+
await fs25.promises.unlink(tempPath);
|
|
348329
|
+
} catch {
|
|
348330
|
+
}
|
|
348331
|
+
}
|
|
348332
|
+
return archive.filename;
|
|
348160
348333
|
}
|
|
348161
348334
|
archiveSession() {
|
|
348162
348335
|
return this.writeSessionArchive(this.chatMessages, this.modeName(), this.model);
|
|
@@ -348224,6 +348397,93 @@ ${msg.content}
|
|
|
348224
348397
|
}
|
|
348225
348398
|
return filename;
|
|
348226
348399
|
}
|
|
348400
|
+
/**
|
|
348401
|
+
* Non-blocking archive writer used by the desktop IPC path. The conversation
|
|
348402
|
+
* state merge remains synchronous and lock-protected, but the potentially
|
|
348403
|
+
* large markdown payload and manifest use promise-based filesystem I/O so
|
|
348404
|
+
* independent workspaces can archive in parallel without freezing Electron.
|
|
348405
|
+
*/
|
|
348406
|
+
async archiveConversationAsync(conversationId) {
|
|
348407
|
+
return await this.archiveConversationAsyncUnlocked(conversationId);
|
|
348408
|
+
}
|
|
348409
|
+
async archiveConversationAsyncUnlocked(conversationId) {
|
|
348410
|
+
const ws = this.workspace.current;
|
|
348411
|
+
if (!ws) return null;
|
|
348412
|
+
const clean = this.safeConversationId(conversationId || "default");
|
|
348413
|
+
const stateKey2 = this.workspaceConversationStateKey(clean);
|
|
348414
|
+
if (!stateKey2) return null;
|
|
348415
|
+
const memoryKey = `${ws.isInternal ? "internal" : "external"}:${path28.resolve(ws.path)}::conversation:${clean}`;
|
|
348416
|
+
const archiveDir = path28.join(ws.path, "archive");
|
|
348417
|
+
const workspacePrefix = this.workspaceConversationPrefix() || "";
|
|
348418
|
+
const archiveMode = this.modeName();
|
|
348419
|
+
const archiveModel = this.model;
|
|
348420
|
+
const cachedStored = this.readStoredConversationState(ws);
|
|
348421
|
+
const stored = JSON.parse(JSON.stringify(cachedStored || {}));
|
|
348422
|
+
const persisted = stored.conversations?.[stateKey2];
|
|
348423
|
+
if (persisted) this.normalizeConversationTree(persisted);
|
|
348424
|
+
const memory = this.workspaceConversations.get(memoryKey);
|
|
348425
|
+
const persistedMessagesAvailable = persisted?.chatMessages !== void 0;
|
|
348426
|
+
const sourceMessages = persisted?.chatMessages ?? memory?.chatMessages ?? [];
|
|
348427
|
+
const sourceHistory = persistedMessagesAvailable ? persisted?.history ?? [] : memory?.history ?? persisted?.history ?? [];
|
|
348428
|
+
const messages = this.normalizeConversationChatMessages(sourceMessages, sourceHistory);
|
|
348429
|
+
const filename = await this.writeSessionArchiveAsync(messages, archiveMode, archiveModel, archiveDir);
|
|
348430
|
+
const archiveEntry = persisted ? JSON.parse(JSON.stringify(persisted)) : {
|
|
348431
|
+
title: this.titleFromMessages(messages, clean),
|
|
348432
|
+
chatMessages: messages,
|
|
348433
|
+
history: sourceHistory,
|
|
348434
|
+
plan: memory?.plan,
|
|
348435
|
+
linkedPlan: memory?.linkedPlan,
|
|
348436
|
+
subagentState: memory?.subagentState,
|
|
348437
|
+
workRuns: memory?.workRuns,
|
|
348438
|
+
continuations: memory?.continuations,
|
|
348439
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
348440
|
+
};
|
|
348441
|
+
const manifest = {
|
|
348442
|
+
version: 2,
|
|
348443
|
+
kind: "newmark-conversation-archive",
|
|
348444
|
+
archivedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
348445
|
+
conversationId: clean,
|
|
348446
|
+
workspaceId: ws.id,
|
|
348447
|
+
workspaceName: ws.name,
|
|
348448
|
+
workspacePath: ws.path,
|
|
348449
|
+
workspaceInternal: ws.isInternal,
|
|
348450
|
+
statePrefix: workspacePrefix,
|
|
348451
|
+
entry: this.conversationEntryForDisk(archiveEntry)
|
|
348452
|
+
};
|
|
348453
|
+
const manifestPath = this.archiveManifestPath(path28.join(archiveDir, filename));
|
|
348454
|
+
const manifestTempPath = `${manifestPath}.${process.pid}.${crypto14.randomUUID()}.tmp`;
|
|
348455
|
+
try {
|
|
348456
|
+
await fs25.promises.writeFile(manifestTempPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
348457
|
+
await fs25.promises.rename(manifestTempPath, manifestPath);
|
|
348458
|
+
} finally {
|
|
348459
|
+
try {
|
|
348460
|
+
await fs25.promises.unlink(manifestTempPath);
|
|
348461
|
+
} catch {
|
|
348462
|
+
}
|
|
348463
|
+
}
|
|
348464
|
+
this.finalizeAsyncConversationArchive(clean, stateKey2, memoryKey, ws);
|
|
348465
|
+
return filename;
|
|
348466
|
+
}
|
|
348467
|
+
finalizeAsyncConversationArchive(clean, stateKey2, memoryKey, ws) {
|
|
348468
|
+
let nextActiveId = "";
|
|
348469
|
+
this.mutateStoredConversationState(ws, (latest) => {
|
|
348470
|
+
latest.conversations = latest.conversations || {};
|
|
348471
|
+
delete latest.conversations[stateKey2];
|
|
348472
|
+
const prefix = stateKey2.slice(0, Math.max(0, stateKey2.length - clean.length - 1)) + "-";
|
|
348473
|
+
const remaining = Object.keys(latest.conversations).filter((key3) => !prefix || key3.startsWith(prefix)).map((key3) => key3.slice(prefix.length)).filter(Boolean);
|
|
348474
|
+
const currentActiveId = this.safeConversationId(latest.activeConversationId || this.activeConversationId || "default");
|
|
348475
|
+
if (clean === currentActiveId) latest.activeConversationId = remaining[0] || "default";
|
|
348476
|
+
nextActiveId = latest.activeConversationId || remaining[0] || "default";
|
|
348477
|
+
return latest;
|
|
348478
|
+
});
|
|
348479
|
+
this.workspaceConversations.delete(memoryKey);
|
|
348480
|
+
const duplicateMemoryKey = `${ws.isInternal ? "internal" : "external"}:${path28.resolve(ws.path)}::conversation:${clean}`;
|
|
348481
|
+
this.workspaceConversations.delete(duplicateMemoryKey);
|
|
348482
|
+
if (clean === this.safeConversationId(this.activeConversationId || "default")) {
|
|
348483
|
+
this.activeConversationId = nextActiveId || "default";
|
|
348484
|
+
this.loadWorkspaceConversationState();
|
|
348485
|
+
}
|
|
348486
|
+
}
|
|
348227
348487
|
listStoredConversationIds(stored) {
|
|
348228
348488
|
const prefix = `${this.workspaceConversationPrefix() || ""}-`;
|
|
348229
348489
|
return Object.keys(stored.conversations || {}).filter((key3) => !prefix || key3.startsWith(prefix)).map((key3) => key3.slice(prefix.length)).filter(Boolean);
|
|
@@ -348749,9 +349009,9 @@ ${msg.content}
|
|
|
348749
349009
|
const provider = this.config.findProvider(providerId);
|
|
348750
349010
|
return all.filter((m2) => m2.provider_id === (provider?.id || providerId));
|
|
348751
349011
|
}
|
|
348752
|
-
async validateModels(selectedNames) {
|
|
349012
|
+
async validateModels(selectedNames, options = {}) {
|
|
348753
349013
|
if (this.modelValidationPromise) return this.modelValidationPromise;
|
|
348754
|
-
const validation = this.runModelValidation(selectedNames);
|
|
349014
|
+
const validation = this.runModelValidation(selectedNames, options.persist !== false);
|
|
348755
349015
|
this.modelValidationPromise = validation;
|
|
348756
349016
|
try {
|
|
348757
349017
|
return await validation;
|
|
@@ -348769,7 +349029,7 @@ ${msg.content}
|
|
|
348769
349029
|
recentChecks: this.modelValidationProgress.recentChecks.map((item) => ({ ...item }))
|
|
348770
349030
|
};
|
|
348771
349031
|
}
|
|
348772
|
-
async runModelValidation(selectedNames) {
|
|
349032
|
+
async runModelValidation(selectedNames, persist = true) {
|
|
348773
349033
|
const selectedModels = this.config.modelsForSelections(selectedNames);
|
|
348774
349034
|
if (!selectedModels.length) {
|
|
348775
349035
|
this.modelValidationProgress = {
|
|
@@ -348787,7 +349047,7 @@ ${msg.content}
|
|
|
348787
349047
|
}
|
|
348788
349048
|
const results = [];
|
|
348789
349049
|
const catalogByProvider = /* @__PURE__ */ new Map();
|
|
348790
|
-
const cache = new FileModelValidationCache(this.rootPath);
|
|
349050
|
+
const cache = new FileModelValidationCache(this.rootPath, { readOnly: !persist });
|
|
348791
349051
|
const checksPerModel = 11;
|
|
348792
349052
|
let currentModel = "";
|
|
348793
349053
|
let currentModelChecks = 0;
|
|
@@ -348919,7 +349179,7 @@ ${msg.content}
|
|
|
348919
349179
|
completedModels: this.modelValidationProgress.completedModels + 1
|
|
348920
349180
|
};
|
|
348921
349181
|
}
|
|
348922
|
-
this.config.save();
|
|
349182
|
+
if (persist) this.config.save();
|
|
348923
349183
|
this.modelValidationProgress = {
|
|
348924
349184
|
...this.modelValidationProgress,
|
|
348925
349185
|
running: false,
|
|
@@ -349171,7 +349431,8 @@ ${String(input.content || "").slice(0, 18e3)}`;
|
|
|
349171
349431
|
const text = typeof input === "string" ? input : String(input.text || "");
|
|
349172
349432
|
const inputEnvelope = typeof input === "string" ? null : input;
|
|
349173
349433
|
const hiddenUserInput = inputEnvelope?.hiddenUserInput === true;
|
|
349174
|
-
this.
|
|
349434
|
+
const explicitFixedModel = this.model !== "" && this.model !== "auto";
|
|
349435
|
+
if (!explicitFixedModel) this.ensureUsableModelSelection();
|
|
349175
349436
|
const clientMessageId = String(inputEnvelope?.clientMessageId || "").trim();
|
|
349176
349437
|
const inputRunId = String(inputEnvelope?.runId || this.activeWorkRunId || "").trim();
|
|
349177
349438
|
const rawImages = typeof input === "string" ? [] : Array.isArray(input.images) ? input.images : [];
|
|
@@ -349259,7 +349520,13 @@ ${String(input.content || "").slice(0, 18e3)}`;
|
|
|
349259
349520
|
await this.evaluateAndSwitch(displayText, inputEnvelope?.routePolicy);
|
|
349260
349521
|
}
|
|
349261
349522
|
if (this.model && this.modelIsUnavailable(this.model)) {
|
|
349523
|
+
const requestedModel = this.model;
|
|
349262
349524
|
this.switchToFallbackModel();
|
|
349525
|
+
if (this.modelIsUnavailable(this.model)) {
|
|
349526
|
+
const message = `[Error] Model '${requestedModel || "unknown"}' is unavailable or not configured. Select a configured model or enable a valid provider before sending.`;
|
|
349527
|
+
this.status = "error";
|
|
349528
|
+
throw new Error(message);
|
|
349529
|
+
}
|
|
349263
349530
|
}
|
|
349264
349531
|
if (this.engine === "opencode") {
|
|
349265
349532
|
if (images.length) return [{ type: "text", text: "[Vision unavailable] The OpenCode engine does not accept Newmark image attachments." }];
|
|
@@ -350252,11 +350519,11 @@ Falling back to built-in engine.` }];
|
|
|
350252
350519
|
}
|
|
350253
350520
|
}
|
|
350254
350521
|
async maybeCompress(msgs, provider, signal, compressionModel, force = false) {
|
|
350255
|
-
if (signal?.aborted) return;
|
|
350256
|
-
if (!this.config.getBool("context", "auto_compress")) return;
|
|
350522
|
+
if (signal?.aborted) return false;
|
|
350523
|
+
if (!this.config.getBool("context", "auto_compress")) return false;
|
|
350257
350524
|
const total = msgs.reduce((sum, m2) => sum + (typeof m2.content === "string" ? m2.content.length : JSON.stringify(m2.content || "").length), 0);
|
|
350258
350525
|
const budget = this.compressionBudget(msgs);
|
|
350259
|
-
if (budget.estimatedTokens < budget.triggerTokens && !force) return;
|
|
350526
|
+
if (budget.estimatedTokens < budget.triggerTokens && !force) return false;
|
|
350260
350527
|
if (!force && this.lastCompression && String(msgs[0]?.content || "").includes(this.lastCompression.summary)) {
|
|
350261
350528
|
const baselineChars = Math.max(0, Number(this.lastCompression.compressedChars || 0));
|
|
350262
350529
|
const baselineTokens = Math.max(0, Number(this.lastCompression.compressedTokens || 0));
|
|
@@ -350264,16 +350531,16 @@ Falling back to built-in engine.` }];
|
|
|
350264
350531
|
const tokenGrowth = baselineTokens ? Math.max(0, budget.estimatedTokens - baselineTokens) : Number.POSITIVE_INFINITY;
|
|
350265
350532
|
const minCharGrowth = Math.max(12e3, Math.floor(baselineChars * 0.25));
|
|
350266
350533
|
const minTokenGrowth = Math.max(1024, Math.floor(budget.triggerTokens * 0.2));
|
|
350267
|
-
if (charGrowth < minCharGrowth && tokenGrowth < minTokenGrowth) return;
|
|
350534
|
+
if (charGrowth < minCharGrowth && tokenGrowth < minTokenGrowth) return false;
|
|
350268
350535
|
}
|
|
350269
350536
|
const originalMessageCount = msgs.length;
|
|
350270
350537
|
const configuredKeepLast = this.config.getNum("context", "keep_recent_messages") || 10;
|
|
350271
|
-
if (msgs.length <= 1) return;
|
|
350538
|
+
if (msgs.length <= 1) return false;
|
|
350272
350539
|
const continuationAnchorTokens = this.estimateContextTokens([this.postCompressionContinuationMessage()]);
|
|
350273
350540
|
const recentBudget = Math.max(64, budget.targetTokens - budget.summaryTokens - continuationAnchorTokens);
|
|
350274
350541
|
const recent = this.recentContextSuffix(msgs, configuredKeepLast, recentBudget);
|
|
350275
350542
|
const recentStart = Math.max(0, msgs.length - recent.length);
|
|
350276
|
-
if (recentStart <= 0) return;
|
|
350543
|
+
if (recentStart <= 0) return false;
|
|
350277
350544
|
const middle = msgs.slice(0, recentStart);
|
|
350278
350545
|
const currentInstruction = this.latestUserHistoryText(recent);
|
|
350279
350546
|
const compression = await this.buildCompressionSummary(
|
|
@@ -350285,7 +350552,7 @@ Falling back to built-in engine.` }];
|
|
|
350285
350552
|
compressionModel || this.activeModelName(),
|
|
350286
350553
|
currentInstruction
|
|
350287
350554
|
);
|
|
350288
|
-
if (signal?.aborted) return;
|
|
350555
|
+
if (signal?.aborted) return false;
|
|
350289
350556
|
const compressed = [{
|
|
350290
350557
|
role: "system",
|
|
350291
350558
|
content: compression.summary
|
|
@@ -350310,6 +350577,7 @@ Falling back to built-in engine.` }];
|
|
|
350310
350577
|
};
|
|
350311
350578
|
this.pushCompressionCacheEntry(compression.summary, middle, compression.model, compression.fallback);
|
|
350312
350579
|
this.persistCompressedHistory(compression.summary, recent.length, msgs);
|
|
350580
|
+
return true;
|
|
350313
350581
|
}
|
|
350314
350582
|
async buildCompressionSummary(middle, totalChars, budget, provider, signal, compressionModel, currentInstruction = "") {
|
|
350315
350583
|
const workspacePath = this.workspace.current?.path || this.rootPath;
|
|
@@ -351645,7 +351913,12 @@ var ConversationKernel = class {
|
|
|
351645
351913
|
if (runtime.runId === runId && runtime.stopRequestedRunId === runId) {
|
|
351646
351914
|
stopped = true;
|
|
351647
351915
|
} else {
|
|
351648
|
-
runtime.runner.finishConversationWorkRun(
|
|
351916
|
+
runtime.runner.finishConversationWorkRun(
|
|
351917
|
+
runId,
|
|
351918
|
+
"error",
|
|
351919
|
+
void 0,
|
|
351920
|
+
error instanceof Error ? error.message : String(error)
|
|
351921
|
+
);
|
|
351649
351922
|
throw error;
|
|
351650
351923
|
}
|
|
351651
351924
|
} finally {
|