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;
|
|
@@ -327623,8 +327628,10 @@ var ConfigManager = class {
|
|
|
327623
327628
|
rootPath;
|
|
327624
327629
|
config;
|
|
327625
327630
|
workspaceOverrides;
|
|
327626
|
-
|
|
327631
|
+
readOnly;
|
|
327632
|
+
constructor(rootPath, options = {}) {
|
|
327627
327633
|
this.rootPath = rootPath;
|
|
327634
|
+
this.readOnly = options.readOnly === true;
|
|
327628
327635
|
this.workspaceOverrides = /* @__PURE__ */ new Map();
|
|
327629
327636
|
this.config = this.load();
|
|
327630
327637
|
}
|
|
@@ -327639,17 +327646,19 @@ var ConfigManager = class {
|
|
|
327639
327646
|
const raw = JSON.parse(readJsonText(cp));
|
|
327640
327647
|
const normalized = normalizeConfigShape(raw, true);
|
|
327641
327648
|
if (isCorruptConfig(raw, normalized)) {
|
|
327649
|
+
if (this.readOnly) return defaultConfig();
|
|
327642
327650
|
this.backupConfig(cp, "invalid-shape");
|
|
327643
327651
|
return this.writeRecoveredConfig(cp);
|
|
327644
327652
|
}
|
|
327645
327653
|
if (migrateProviderIdsInConfig(normalized)) {
|
|
327646
327654
|
try {
|
|
327647
|
-
fs3.writeFileSync(cp, JSON.stringify(normalized, null, 2), "utf-8");
|
|
327655
|
+
if (!this.readOnly) fs3.writeFileSync(cp, JSON.stringify(normalized, null, 2), "utf-8");
|
|
327648
327656
|
} catch {
|
|
327649
327657
|
}
|
|
327650
327658
|
}
|
|
327651
327659
|
return normalized;
|
|
327652
327660
|
} catch {
|
|
327661
|
+
if (this.readOnly) return defaultConfig();
|
|
327653
327662
|
this.backupConfig(cp, "invalid-json");
|
|
327654
327663
|
return this.writeRecoveredConfig(cp);
|
|
327655
327664
|
}
|
|
@@ -327695,10 +327704,12 @@ var ConfigManager = class {
|
|
|
327695
327704
|
this.config[section][key3] = { value: normalizedValue };
|
|
327696
327705
|
}
|
|
327697
327706
|
save() {
|
|
327707
|
+
if (this.readOnly) return;
|
|
327698
327708
|
const j2 = JSON.stringify(this.config, null, 2);
|
|
327699
327709
|
fs3.writeFileSync(path3.join(this.rootPath, "config.json"), j2, "utf-8");
|
|
327700
327710
|
}
|
|
327701
327711
|
saveTo(targetPath) {
|
|
327712
|
+
if (this.readOnly) return;
|
|
327702
327713
|
const j2 = JSON.stringify(this.config, null, 2);
|
|
327703
327714
|
fs3.writeFileSync(targetPath, j2, "utf-8");
|
|
327704
327715
|
}
|
|
@@ -327923,12 +327934,14 @@ var ConfigManager = class {
|
|
|
327923
327934
|
return providers;
|
|
327924
327935
|
}
|
|
327925
327936
|
writeRecoveredConfig(configPath) {
|
|
327937
|
+
if (this.readOnly) return defaultConfig();
|
|
327926
327938
|
const config = loadExampleConfig();
|
|
327927
327939
|
fs3.mkdirSync(path3.dirname(configPath), { recursive: true });
|
|
327928
327940
|
fs3.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
327929
327941
|
return config;
|
|
327930
327942
|
}
|
|
327931
327943
|
backupConfig(configPath, reason) {
|
|
327944
|
+
if (this.readOnly) return;
|
|
327932
327945
|
try {
|
|
327933
327946
|
if (!fs3.existsSync(configPath)) return;
|
|
327934
327947
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -328273,7 +328286,10 @@ function defaultConfig() {
|
|
|
328273
328286
|
general: {
|
|
328274
328287
|
tone: { _description: "Conversation style", _type: "choice", _values: ["strict_simple", "casual_friendly"], value: "strict_simple" },
|
|
328275
328288
|
language: { _description: "Default language", _type: "choice", _values: ["en", "zh", "auto"], value: "auto" },
|
|
328276
|
-
|
|
328289
|
+
// A first-run desktop window must have a deterministic close/exit
|
|
328290
|
+
// contract. Users who explicitly choose minimize-to-tray keep that
|
|
328291
|
+
// choice, but a fresh install must not hide the process on OS close.
|
|
328292
|
+
close_behavior: { _description: "Close behavior", _type: "choice", _values: ["minimize", "exit"], value: "exit" },
|
|
328277
328293
|
default_input: { _description: "Default input mode", _type: "choice", _values: ["guide", "next"], value: "guide" },
|
|
328278
328294
|
auto_archive_on_close: { _description: "Auto archive on close", _type: "boolean", value: true }
|
|
328279
328295
|
},
|
|
@@ -328612,6 +328628,38 @@ function providerAbortError(signal) {
|
|
|
328612
328628
|
if (!error.name || error.name === "Error") error.name = "AbortError";
|
|
328613
328629
|
return error;
|
|
328614
328630
|
}
|
|
328631
|
+
function providerStreamTimeoutError(timeoutMs) {
|
|
328632
|
+
const error = new Error("Stream read timeout");
|
|
328633
|
+
error.name = "TimeoutError";
|
|
328634
|
+
error.message = `Stream read timeout after ${timeoutMs}ms`;
|
|
328635
|
+
return error;
|
|
328636
|
+
}
|
|
328637
|
+
async function readProviderStreamChunk(reader, signal, timeoutMs = 3e4) {
|
|
328638
|
+
if (signal.aborted) throw providerAbortError(signal);
|
|
328639
|
+
let timer;
|
|
328640
|
+
let onAbort;
|
|
328641
|
+
const abortPromise = new Promise((_3, reject) => {
|
|
328642
|
+
onAbort = () => reject(providerAbortError(signal));
|
|
328643
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
328644
|
+
});
|
|
328645
|
+
const timeoutPromise = new Promise((_3, reject) => {
|
|
328646
|
+
timer = setTimeout(() => reject(providerStreamTimeoutError(timeoutMs)), timeoutMs);
|
|
328647
|
+
});
|
|
328648
|
+
try {
|
|
328649
|
+
return await Promise.race([reader.read(), abortPromise, timeoutPromise]);
|
|
328650
|
+
} catch (error) {
|
|
328651
|
+
if (signal.aborted || error instanceof Error && error.name === "TimeoutError") {
|
|
328652
|
+
try {
|
|
328653
|
+
await reader.cancel(error);
|
|
328654
|
+
} catch {
|
|
328655
|
+
}
|
|
328656
|
+
}
|
|
328657
|
+
throw error;
|
|
328658
|
+
} finally {
|
|
328659
|
+
if (timer) clearTimeout(timer);
|
|
328660
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
328661
|
+
}
|
|
328662
|
+
}
|
|
328615
328663
|
function parseProviderSse(raw) {
|
|
328616
328664
|
const events = [];
|
|
328617
328665
|
for (const block of String(raw || "").replace(/\r\n/g, "\n").split(/\n\n+/)) {
|
|
@@ -328847,12 +328895,7 @@ var ChatCompletionsAdapter = class {
|
|
|
328847
328895
|
let emittedTool = false;
|
|
328848
328896
|
try {
|
|
328849
328897
|
while (true) {
|
|
328850
|
-
|
|
328851
|
-
const readPromise = reader.read();
|
|
328852
|
-
const timeoutPromise = new Promise(
|
|
328853
|
-
(_3, reject) => setTimeout(() => reject(new Error("Stream read timeout")), 3e4)
|
|
328854
|
-
);
|
|
328855
|
-
const { done, value } = await Promise.race([readPromise, timeoutPromise]);
|
|
328898
|
+
const { done, value } = await readProviderStreamChunk(reader, signal);
|
|
328856
328899
|
if (done) break;
|
|
328857
328900
|
buffer += decoder.decode(value, { stream: true });
|
|
328858
328901
|
const lines = buffer.split("\n");
|
|
@@ -329090,8 +329133,7 @@ var ResponsesAdapter = class {
|
|
|
329090
329133
|
let streamError = "";
|
|
329091
329134
|
try {
|
|
329092
329135
|
while (true) {
|
|
329093
|
-
|
|
329094
|
-
const { done, value } = await reader.read();
|
|
329136
|
+
const { done, value } = await readProviderStreamChunk(reader, signal);
|
|
329095
329137
|
if (done) break;
|
|
329096
329138
|
buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
|
|
329097
329139
|
const blocks = buffer.split(/\n\n+/);
|
|
@@ -329301,6 +329343,16 @@ function createProviderAdapter(providerId, apiMode) {
|
|
|
329301
329343
|
}
|
|
329302
329344
|
|
|
329303
329345
|
// src/llm/provider.ts
|
|
329346
|
+
var DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS = 9e4;
|
|
329347
|
+
var MIN_PROVIDER_REQUEST_TIMEOUT_MS = 50;
|
|
329348
|
+
function providerTimeoutError(timeoutMs) {
|
|
329349
|
+
const error = new Error(`Provider request timed out after ${timeoutMs}ms`);
|
|
329350
|
+
error.name = "TimeoutError";
|
|
329351
|
+
return error;
|
|
329352
|
+
}
|
|
329353
|
+
function isProviderTimeoutError(error) {
|
|
329354
|
+
return error instanceof Error && error.name === "TimeoutError";
|
|
329355
|
+
}
|
|
329304
329356
|
function abortFailure(signal) {
|
|
329305
329357
|
const reason = signal?.reason;
|
|
329306
329358
|
const error = reason instanceof Error ? reason : new Error(reason ? String(reason) : "LLM request aborted");
|
|
@@ -329330,13 +329382,14 @@ function parseProviderSse2(raw) {
|
|
|
329330
329382
|
return events;
|
|
329331
329383
|
}
|
|
329332
329384
|
var LLMProvider = class _LLMProvider {
|
|
329333
|
-
constructor(name50, baseUrl, apiKey, explicitProtocol, openAIMode = "chat_stream", useProviderAdaptersV2 = false) {
|
|
329385
|
+
constructor(name50, baseUrl, apiKey, explicitProtocol, openAIMode = "chat_stream", useProviderAdaptersV2 = false, requestTimeoutMs = DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS) {
|
|
329334
329386
|
this.name = name50;
|
|
329335
329387
|
this.baseUrl = baseUrl;
|
|
329336
329388
|
this.apiKey = apiKey;
|
|
329337
329389
|
this.explicitProtocol = explicitProtocol;
|
|
329338
329390
|
this.openAIMode = openAIMode;
|
|
329339
329391
|
this.useProviderAdaptersV2 = useProviderAdaptersV2;
|
|
329392
|
+
this.requestTimeoutMs = requestTimeoutMs;
|
|
329340
329393
|
}
|
|
329341
329394
|
name;
|
|
329342
329395
|
baseUrl;
|
|
@@ -329344,8 +329397,25 @@ var LLMProvider = class _LLMProvider {
|
|
|
329344
329397
|
explicitProtocol;
|
|
329345
329398
|
openAIMode;
|
|
329346
329399
|
useProviderAdaptersV2;
|
|
329400
|
+
requestTimeoutMs;
|
|
329347
329401
|
static nodeHttpTransport = null;
|
|
329348
329402
|
static powershellTransport = null;
|
|
329403
|
+
effectiveRequestTimeout(timeoutMs) {
|
|
329404
|
+
const requested = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS;
|
|
329405
|
+
const configured = Number.isFinite(this.requestTimeoutMs) && this.requestTimeoutMs > 0 ? this.requestTimeoutMs : DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS;
|
|
329406
|
+
return Math.max(MIN_PROVIDER_REQUEST_TIMEOUT_MS, Math.min(requested, configured));
|
|
329407
|
+
}
|
|
329408
|
+
async withRequestTimeout(promise, timeoutMs, signal) {
|
|
329409
|
+
let timer;
|
|
329410
|
+
const timeoutPromise = new Promise((_3, reject) => {
|
|
329411
|
+
timer = setTimeout(() => reject(providerTimeoutError(timeoutMs)), timeoutMs);
|
|
329412
|
+
});
|
|
329413
|
+
try {
|
|
329414
|
+
return await abortable(Promise.race([promise, timeoutPromise]), signal);
|
|
329415
|
+
} finally {
|
|
329416
|
+
if (timer) clearTimeout(timer);
|
|
329417
|
+
}
|
|
329418
|
+
}
|
|
329349
329419
|
intelligenceConfig(tier) {
|
|
329350
329420
|
switch (tier) {
|
|
329351
329421
|
case "low":
|
|
@@ -329447,6 +329517,7 @@ var LLMProvider = class _LLMProvider {
|
|
|
329447
329517
|
};
|
|
329448
329518
|
}
|
|
329449
329519
|
async postJsonWithFetchFallback(url, headers, body, timeoutMs = 12e4, signal) {
|
|
329520
|
+
const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
|
|
329450
329521
|
if (this.isPlainHttpLoopback(url)) {
|
|
329451
329522
|
const pathname = (() => {
|
|
329452
329523
|
try {
|
|
@@ -329456,7 +329527,7 @@ var LLMProvider = class _LLMProvider {
|
|
|
329456
329527
|
}
|
|
329457
329528
|
})();
|
|
329458
329529
|
this.transportDiagnostic("loopback:start", pathname);
|
|
329459
|
-
const local = await this.nodeHttpJson("POST", url, headers, JSON.stringify(body), signal);
|
|
329530
|
+
const local = await this.nodeHttpJson("POST", url, headers, JSON.stringify(body), signal, effectiveTimeout);
|
|
329460
329531
|
this.transportDiagnostic("loopback:complete", `status=${local.status} bytes=${Buffer.byteLength(local.body || "")}`);
|
|
329461
329532
|
return {
|
|
329462
329533
|
ok: local.status >= 200 && local.status < 300,
|
|
@@ -329470,7 +329541,7 @@ var LLMProvider = class _LLMProvider {
|
|
|
329470
329541
|
const forwardAbort = () => abort.abort(signal?.reason);
|
|
329471
329542
|
if (signal?.aborted) forwardAbort();
|
|
329472
329543
|
else signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
329473
|
-
const timer = setTimeout(() => abort.abort(),
|
|
329544
|
+
const timer = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
|
|
329474
329545
|
try {
|
|
329475
329546
|
const response = await fetch(url, {
|
|
329476
329547
|
method: "POST",
|
|
@@ -329481,8 +329552,9 @@ var LLMProvider = class _LLMProvider {
|
|
|
329481
329552
|
return response;
|
|
329482
329553
|
} catch (e3) {
|
|
329483
329554
|
if (signal?.aborted) throw abortFailure(signal);
|
|
329555
|
+
if (abort.signal.aborted) throw abortFailure(abort.signal);
|
|
329484
329556
|
if (!this.shouldUseNodeHttpFallback(e3)) throw e3;
|
|
329485
|
-
const fallback = await this.nodeHttpJson("POST", url, headers, JSON.stringify(body), signal);
|
|
329557
|
+
const fallback = await this.nodeHttpJson("POST", url, headers, JSON.stringify(body), signal, effectiveTimeout);
|
|
329486
329558
|
return {
|
|
329487
329559
|
ok: fallback.status >= 200 && fallback.status < 300,
|
|
329488
329560
|
status: fallback.status,
|
|
@@ -329496,14 +329568,16 @@ var LLMProvider = class _LLMProvider {
|
|
|
329496
329568
|
}
|
|
329497
329569
|
}
|
|
329498
329570
|
async getJsonWithFetchFallback(url, headers, timeoutMs = 3e4) {
|
|
329571
|
+
const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
|
|
329499
329572
|
const abort = new AbortController();
|
|
329500
|
-
const timer = setTimeout(() => abort.abort(),
|
|
329573
|
+
const timer = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
|
|
329501
329574
|
try {
|
|
329502
329575
|
const response = await fetch(url, { method: "GET", headers, signal: abort.signal });
|
|
329503
329576
|
return response;
|
|
329504
329577
|
} catch (e3) {
|
|
329578
|
+
if (abort.signal.aborted) throw abortFailure(abort.signal);
|
|
329505
329579
|
if (!this.shouldUseNodeHttpFallback(e3)) throw e3;
|
|
329506
|
-
const fallback = await this.nodeHttpJson("GET", url, headers);
|
|
329580
|
+
const fallback = await this.nodeHttpJson("GET", url, headers, "", void 0, effectiveTimeout);
|
|
329507
329581
|
return {
|
|
329508
329582
|
ok: fallback.status >= 200 && fallback.status < 300,
|
|
329509
329583
|
status: fallback.status,
|
|
@@ -329516,14 +329590,16 @@ var LLMProvider = class _LLMProvider {
|
|
|
329516
329590
|
}
|
|
329517
329591
|
}
|
|
329518
329592
|
shouldUseNodeHttpFallback(error) {
|
|
329519
|
-
return error instanceof TypeError && /fetch failed/i.test(error.message)
|
|
329593
|
+
return error instanceof TypeError && /fetch failed/i.test(error.message);
|
|
329520
329594
|
}
|
|
329521
|
-
nodeHttpJson(method, urlValue, headers, body = "", signal) {
|
|
329595
|
+
nodeHttpJson(method, urlValue, headers, body = "", signal, timeoutMs = DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS) {
|
|
329596
|
+
const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
|
|
329522
329597
|
if (_LLMProvider.nodeHttpTransport) {
|
|
329523
|
-
return
|
|
329598
|
+
return this.withRequestTimeout(_LLMProvider.nodeHttpTransport(method, urlValue, headers, body), effectiveTimeout, signal).catch((error) => {
|
|
329524
329599
|
if (signal?.aborted) throw abortFailure(signal);
|
|
329600
|
+
if (isProviderTimeoutError(error)) throw error;
|
|
329525
329601
|
if (process.platform === "win32") {
|
|
329526
|
-
return this.powershellJson(method, urlValue, headers, body, signal);
|
|
329602
|
+
return this.powershellJson(method, urlValue, headers, body, signal, effectiveTimeout);
|
|
329527
329603
|
}
|
|
329528
329604
|
throw error;
|
|
329529
329605
|
});
|
|
@@ -329568,8 +329644,8 @@ var LLMProvider = class _LLMProvider {
|
|
|
329568
329644
|
else fail(new Error("Node HTTP response closed before completion"));
|
|
329569
329645
|
});
|
|
329570
329646
|
});
|
|
329571
|
-
req.setTimeout(
|
|
329572
|
-
req.destroy(
|
|
329647
|
+
req.setTimeout(effectiveTimeout, () => {
|
|
329648
|
+
req.destroy(providerTimeoutError(effectiveTimeout));
|
|
329573
329649
|
});
|
|
329574
329650
|
req.on("error", reject);
|
|
329575
329651
|
const onAbort = () => req.destroy(abortFailure(signal));
|
|
@@ -329580,15 +329656,17 @@ var LLMProvider = class _LLMProvider {
|
|
|
329580
329656
|
req.end();
|
|
329581
329657
|
}).catch((error) => {
|
|
329582
329658
|
if (signal?.aborted) throw abortFailure(signal);
|
|
329659
|
+
if (isProviderTimeoutError(error)) throw error;
|
|
329583
329660
|
if (process.platform === "win32") {
|
|
329584
|
-
return this.powershellJson(method, urlValue, headers, body, signal);
|
|
329661
|
+
return this.powershellJson(method, urlValue, headers, body, signal, effectiveTimeout);
|
|
329585
329662
|
}
|
|
329586
329663
|
throw error;
|
|
329587
329664
|
});
|
|
329588
329665
|
}
|
|
329589
|
-
powershellJson(method, urlValue, headers, body = "", signal) {
|
|
329666
|
+
powershellJson(method, urlValue, headers, body = "", signal, timeoutMs = DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS) {
|
|
329667
|
+
const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
|
|
329590
329668
|
if (_LLMProvider.powershellTransport) {
|
|
329591
|
-
return _LLMProvider.powershellTransport(method, urlValue, headers, body);
|
|
329669
|
+
return this.withRequestTimeout(_LLMProvider.powershellTransport(method, urlValue, headers, body), effectiveTimeout, signal);
|
|
329592
329670
|
}
|
|
329593
329671
|
return new Promise((resolve16, reject) => {
|
|
329594
329672
|
const headerJson = JSON.stringify(headers);
|
|
@@ -329617,7 +329695,7 @@ var LLMProvider = class _LLMProvider {
|
|
|
329617
329695
|
" $raw = $headerJson | ConvertFrom-Json",
|
|
329618
329696
|
" foreach ($p in $raw.PSObject.Properties) { $headers[$p.Name] = [string]$p.Value }",
|
|
329619
329697
|
"}",
|
|
329620
|
-
|
|
329698
|
+
`'$params = @{ Uri = $uri; Method = $method; Headers = $headers; UseBasicParsing = $true; TimeoutSec = ${Math.max(1, Math.ceil(effectiveTimeout / 1e3))} }`,
|
|
329621
329699
|
'if ($method -eq "POST") { $params["Body"] = $bodyJson }',
|
|
329622
329700
|
'if ($method -eq "POST") { $params["ContentType"] = "application/json; charset=utf-8" }',
|
|
329623
329701
|
"$resp = Invoke-WebRequest @params",
|
|
@@ -329649,8 +329727,8 @@ var LLMProvider = class _LLMProvider {
|
|
|
329649
329727
|
const timer = setTimeout(() => {
|
|
329650
329728
|
child.kill();
|
|
329651
329729
|
cleanup();
|
|
329652
|
-
reject(
|
|
329653
|
-
},
|
|
329730
|
+
reject(providerTimeoutError(effectiveTimeout));
|
|
329731
|
+
}, effectiveTimeout + 5e3);
|
|
329654
329732
|
child.stdout.setEncoding("utf8");
|
|
329655
329733
|
child.stderr.setEncoding("utf8");
|
|
329656
329734
|
child.stdout.on("data", (chunk) => {
|
|
@@ -330098,10 +330176,10 @@ ${responsePath}
|
|
|
330098
330176
|
return this.shouldUseResponsesFallback(Number(match[1]), errorText);
|
|
330099
330177
|
}
|
|
330100
330178
|
/**
|
|
330101
|
-
* Loopback-aware transport injected into adapter `execute`.
|
|
330102
|
-
*
|
|
330103
|
-
*
|
|
330104
|
-
*
|
|
330179
|
+
* Loopback-aware transport injected into adapter `execute`. Streaming
|
|
330180
|
+
* requests retain the fetch-to-node fallback for transport failures, while
|
|
330181
|
+
* a local deadline is returned directly so one request cannot become a
|
|
330182
|
+
* second Windows fallback request.
|
|
330105
330183
|
*/
|
|
330106
330184
|
buildProviderAdapterTransport() {
|
|
330107
330185
|
return async (request, signal) => {
|
|
@@ -330110,7 +330188,8 @@ ${responsePath}
|
|
|
330110
330188
|
const forwardAbort = () => abort.abort(signal?.reason);
|
|
330111
330189
|
if (signal?.aborted) forwardAbort();
|
|
330112
330190
|
else signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
330113
|
-
const
|
|
330191
|
+
const effectiveTimeout = this.effectiveRequestTimeout(12e4);
|
|
330192
|
+
const timer = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
|
|
330114
330193
|
try {
|
|
330115
330194
|
try {
|
|
330116
330195
|
return await fetch(request.url, {
|
|
@@ -330121,6 +330200,7 @@ ${responsePath}
|
|
|
330121
330200
|
});
|
|
330122
330201
|
} catch (error) {
|
|
330123
330202
|
if (signal?.aborted) throw abortFailure(signal);
|
|
330203
|
+
if (abort.signal.aborted) throw abortFailure(abort.signal);
|
|
330124
330204
|
if (!this.shouldUseNodeHttpFallback(error)) throw error;
|
|
330125
330205
|
const fallbackHeaders = { ...request.headers };
|
|
330126
330206
|
delete fallbackHeaders["Accept"];
|
|
@@ -330128,7 +330208,7 @@ ${responsePath}
|
|
|
330128
330208
|
request.url,
|
|
330129
330209
|
fallbackHeaders,
|
|
330130
330210
|
{ ...request.body, stream: false },
|
|
330131
|
-
|
|
330211
|
+
effectiveTimeout,
|
|
330132
330212
|
signal
|
|
330133
330213
|
);
|
|
330134
330214
|
return this.toTransportResponse(fallback);
|
|
@@ -330238,7 +330318,8 @@ ${responsePath}
|
|
|
330238
330318
|
const forwardAbort = () => abort.abort(signal?.reason);
|
|
330239
330319
|
if (signal?.aborted) forwardAbort();
|
|
330240
330320
|
else signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
330241
|
-
const
|
|
330321
|
+
const effectiveTimeout = this.effectiveRequestTimeout(12e4);
|
|
330322
|
+
const timeout = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
|
|
330242
330323
|
let reader = null;
|
|
330243
330324
|
try {
|
|
330244
330325
|
let response;
|
|
@@ -330250,9 +330331,10 @@ ${responsePath}
|
|
|
330250
330331
|
signal: abort.signal
|
|
330251
330332
|
});
|
|
330252
330333
|
} catch (e3) {
|
|
330334
|
+
if (signal?.aborted) throw abortFailure(signal);
|
|
330335
|
+
if (abort.signal.aborted) throw abortFailure(abort.signal);
|
|
330253
330336
|
if (!this.shouldUseNodeHttpFallback(e3)) throw e3;
|
|
330254
330337
|
clearTimeout(timeout);
|
|
330255
|
-
if (signal?.aborted) throw abortFailure(signal);
|
|
330256
330338
|
yield* this.githubModelsChatNonStreaming(url, body, signal);
|
|
330257
330339
|
return;
|
|
330258
330340
|
}
|
|
@@ -330272,13 +330354,9 @@ ${responsePath}
|
|
|
330272
330354
|
let currentReasoningContent = "";
|
|
330273
330355
|
let contentPolicyBlocked = false;
|
|
330274
330356
|
let emittedContent = false;
|
|
330357
|
+
const streamSignal = signal || new AbortController().signal;
|
|
330275
330358
|
while (true) {
|
|
330276
|
-
|
|
330277
|
-
const readPromise = reader.read();
|
|
330278
|
-
const timeoutPromise = new Promise(
|
|
330279
|
-
(_3, reject) => setTimeout(() => reject(new Error("Stream read timeout")), 3e4)
|
|
330280
|
-
);
|
|
330281
|
-
const { done, value } = await Promise.race([readPromise, timeoutPromise]);
|
|
330359
|
+
const { done, value } = await readProviderStreamChunk(reader, streamSignal);
|
|
330282
330360
|
if (done) break;
|
|
330283
330361
|
buffer += decoder.decode(value, { stream: true });
|
|
330284
330362
|
const lines = buffer.split("\n");
|
|
@@ -337924,6 +338002,19 @@ var WorkspaceManager = class {
|
|
|
337924
338002
|
this.saveState();
|
|
337925
338003
|
}
|
|
337926
338004
|
}
|
|
338005
|
+
/**
|
|
338006
|
+
* Re-read the registry and persisted current-workspace pointer after another
|
|
338007
|
+
* Newmark entrypoint updates Work/*.json. This intentionally does not create
|
|
338008
|
+
* a workspace: a refresh must reflect the shared on-disk state exactly.
|
|
338009
|
+
*/
|
|
338010
|
+
reloadFromStorage() {
|
|
338011
|
+
if (this.detached) return this.current;
|
|
338012
|
+
this.scan();
|
|
338013
|
+
this.validate();
|
|
338014
|
+
this.current = null;
|
|
338015
|
+
this.restoreCurrent();
|
|
338016
|
+
return this.current;
|
|
338017
|
+
}
|
|
337927
338018
|
saveInternal() {
|
|
337928
338019
|
if (this.detached) return;
|
|
337929
338020
|
const p = path16.join(this.rootPath, "Work", "Local.json");
|
|
@@ -340014,17 +340105,35 @@ function normalizePublicProviderError(error, secrets = []) {
|
|
|
340014
340105
|
}
|
|
340015
340106
|
return raw.slice(0, 1200);
|
|
340016
340107
|
}
|
|
340108
|
+
function throwIfKernelAborted(signal) {
|
|
340109
|
+
if (!signal?.aborted) return;
|
|
340110
|
+
const reason = signal.reason;
|
|
340111
|
+
if (reason instanceof Error) {
|
|
340112
|
+
reason.name = "AbortError";
|
|
340113
|
+
throw reason;
|
|
340114
|
+
}
|
|
340115
|
+
const error = new Error(reason ? String(reason) : "Agent run aborted");
|
|
340116
|
+
error.name = "AbortError";
|
|
340117
|
+
throw error;
|
|
340118
|
+
}
|
|
340017
340119
|
async function runAgentKernel(agent) {
|
|
340018
340120
|
const stopContextTimer = performanceTimer("context_prepare", { conversationId: agent.activeConversationId });
|
|
340121
|
+
const processSignal = agent.activeProcessSignal();
|
|
340122
|
+
if (processSignal?.aborted) {
|
|
340123
|
+
stopContextTimer();
|
|
340124
|
+
throwIfKernelAborted(processSignal);
|
|
340125
|
+
}
|
|
340019
340126
|
if (!agent.engineModel()) {
|
|
340127
|
+
const message = "No LLM configured. Add provider in Settings > Models.";
|
|
340020
340128
|
agent.status = "error";
|
|
340021
340129
|
agent.saveWorkspaceConversationState();
|
|
340022
|
-
|
|
340130
|
+
throw new Error(message);
|
|
340023
340131
|
}
|
|
340024
340132
|
const [{ Agent: NativeAgent }, KernelStreamCompat] = await Promise.all([
|
|
340025
340133
|
Promise.resolve().then(() => (init_agentKernel(), agentKernel_exports)),
|
|
340026
340134
|
Promise.resolve().then(() => (init_stream_types(), stream_types_exports))
|
|
340027
340135
|
]);
|
|
340136
|
+
throwIfKernelAborted(processSignal);
|
|
340028
340137
|
const toolProvisioning = new ToolProvisionSession([], []);
|
|
340029
340138
|
let activeToolSurfaceIdentity = "";
|
|
340030
340139
|
let activeToolSurfaceNotice = "";
|
|
@@ -340050,6 +340159,7 @@ async function runAgentKernel(agent) {
|
|
|
340050
340159
|
const initialToolSurface = refreshToolSurface(true);
|
|
340051
340160
|
const assembledContext = agent.assembleContextV2(initialToolSurface.systemPromptNotice);
|
|
340052
340161
|
const systemPrompt = assembledContext.text;
|
|
340162
|
+
throwIfKernelAborted(processSignal);
|
|
340053
340163
|
let providerRequestCount = 0;
|
|
340054
340164
|
let bootstrappedCompressionAt = agent.lastCompression?.at || "";
|
|
340055
340165
|
stopContextTimer();
|
|
@@ -340070,6 +340180,24 @@ async function runAgentKernel(agent) {
|
|
|
340070
340180
|
kernel2.state.tools = toKernelTools(agent, initialToolSurface.definitions, toolProvisioning);
|
|
340071
340181
|
kernel2.state.messages = toKernelMessages(agent);
|
|
340072
340182
|
agent.attachAgentKernelRuntime(kernel2);
|
|
340183
|
+
let detachProcessAbort = () => {
|
|
340184
|
+
};
|
|
340185
|
+
if (processSignal) {
|
|
340186
|
+
const abortKernel = () => kernel2.abort();
|
|
340187
|
+
if (processSignal.aborted) {
|
|
340188
|
+
kernel2.abort();
|
|
340189
|
+
} else {
|
|
340190
|
+
processSignal.addEventListener("abort", abortKernel, { once: true });
|
|
340191
|
+
detachProcessAbort = () => processSignal.removeEventListener("abort", abortKernel);
|
|
340192
|
+
}
|
|
340193
|
+
}
|
|
340194
|
+
try {
|
|
340195
|
+
throwIfKernelAborted(processSignal);
|
|
340196
|
+
} catch (error) {
|
|
340197
|
+
detachProcessAbort();
|
|
340198
|
+
agent.attachAgentKernelRuntime(null);
|
|
340199
|
+
throw error;
|
|
340200
|
+
}
|
|
340073
340201
|
const tokens = [];
|
|
340074
340202
|
const runOnce = async (promptMessages, appendPromptToAgentHistory) => {
|
|
340075
340203
|
let lastAssistant = null;
|
|
@@ -340173,6 +340301,7 @@ async function runAgentKernel(agent) {
|
|
|
340173
340301
|
else agent.pendingOptions = agent.pendingOptions.filter((question) => !isPlanExecutionQuestion(question));
|
|
340174
340302
|
}
|
|
340175
340303
|
} finally {
|
|
340304
|
+
detachProcessAbort();
|
|
340176
340305
|
agent.attachAgentKernelRuntime(null);
|
|
340177
340306
|
}
|
|
340178
340307
|
agent.status = "idle";
|
|
@@ -340339,20 +340468,18 @@ async function transformContext(agent, messages, signal) {
|
|
|
340339
340468
|
const provider = agent.engineModel();
|
|
340340
340469
|
if (!provider || !compressionModel) return messages;
|
|
340341
340470
|
const newmarkMessages = publicHistoryFromKernelMessages(messages);
|
|
340342
|
-
const beforeCompression = JSON.stringify(newmarkMessages);
|
|
340343
340471
|
const compressionAt = agent.lastCompression?.at || "";
|
|
340344
|
-
await agent.maybeCompress(newmarkMessages, provider, processSignal, compressionModel);
|
|
340472
|
+
let compressed = await agent.maybeCompress(newmarkMessages, provider, processSignal, compressionModel);
|
|
340345
340473
|
if (processSignal?.aborted) return messages;
|
|
340346
|
-
|
|
340347
|
-
|
|
340348
|
-
await agent.maybeCompress(newmarkMessages, null, processSignal, compressionModel, true);
|
|
340474
|
+
if (compressed && agent.estimateContextTokens(newmarkMessages) >= Math.floor(agent.contextWindow(compressionModel).maxTokens * 0.82)) {
|
|
340475
|
+
compressed = await agent.maybeCompress(newmarkMessages, null, processSignal, compressionModel, true) || compressed;
|
|
340349
340476
|
}
|
|
340350
340477
|
const windowMax = agent.contextWindow(compressionModel).maxTokens;
|
|
340351
340478
|
const conservativeTokens = agent.estimateContextTokens(newmarkMessages);
|
|
340352
340479
|
if (conservativeTokens >= Math.floor(windowMax * 0.9) && !processSignal?.aborted) {
|
|
340353
|
-
await agent.maybeCompress(newmarkMessages, null, processSignal, compressionModel, true);
|
|
340480
|
+
compressed = await agent.maybeCompress(newmarkMessages, null, processSignal, compressionModel, true) || compressed;
|
|
340354
340481
|
}
|
|
340355
|
-
if (
|
|
340482
|
+
if (!compressed) return messages;
|
|
340356
340483
|
const durableMessages = toKernelMessagesFromHistory(newmarkMessages, agent);
|
|
340357
340484
|
if (agent.lastCompression?.at && agent.lastCompression.at !== compressionAt) {
|
|
340358
340485
|
agent.recordContextCompressionStep();
|
|
@@ -342243,9 +342370,11 @@ function key2(model) {
|
|
|
342243
342370
|
}
|
|
342244
342371
|
var FileModelValidationCache = class {
|
|
342245
342372
|
filePath;
|
|
342373
|
+
readOnly;
|
|
342246
342374
|
records = /* @__PURE__ */ new Map();
|
|
342247
|
-
constructor(rootPath) {
|
|
342375
|
+
constructor(rootPath, options = {}) {
|
|
342248
342376
|
this.filePath = path20.join(rootPath, "model-validation", "records.json");
|
|
342377
|
+
this.readOnly = options.readOnly === true;
|
|
342249
342378
|
this.load();
|
|
342250
342379
|
}
|
|
342251
342380
|
get(modelKey2) {
|
|
@@ -342254,10 +342383,12 @@ var FileModelValidationCache = class {
|
|
|
342254
342383
|
}
|
|
342255
342384
|
set(record) {
|
|
342256
342385
|
this.records.set(record.modelKey || key2(record.model), JSON.parse(JSON.stringify(record)));
|
|
342386
|
+
if (this.readOnly) return;
|
|
342257
342387
|
this.save();
|
|
342258
342388
|
}
|
|
342259
342389
|
delete(modelKey2) {
|
|
342260
342390
|
if (!this.records.delete(modelKey2)) return;
|
|
342391
|
+
if (this.readOnly) return;
|
|
342261
342392
|
this.save();
|
|
342262
342393
|
}
|
|
342263
342394
|
load() {
|
|
@@ -344133,7 +344264,7 @@ var Agent4 = class _Agent {
|
|
|
344133
344264
|
this.subagentName = options.subagentName || "";
|
|
344134
344265
|
this.subagentPrompt = options.subagentPrompt || "";
|
|
344135
344266
|
this.linkedPlanAccess = options.linkedPlanAccess;
|
|
344136
|
-
this.config = new ConfigManager(rootPath);
|
|
344267
|
+
this.config = new ConfigManager(rootPath, { readOnly: options.readOnlyConfig === true });
|
|
344137
344268
|
this.compressionHistoryArchive = new CompressionHistoryArchive(rootPath);
|
|
344138
344269
|
this.contextV2 = new AgentContextManager(rootPath, this.config);
|
|
344139
344270
|
this.agentRunService = this.config.contextFlag("agent_runtime_v2") ? new AgentRunService(path28.join(rootPath, ".newmark-context-v2")) : null;
|
|
@@ -344664,7 +344795,10 @@ var Agent4 = class _Agent {
|
|
|
344664
344795
|
}
|
|
344665
344796
|
const previousAuto = this.model === "auto" ? this.resolvedDeployment : null;
|
|
344666
344797
|
const qualified = parseDeploymentSelectionValue2(requested);
|
|
344667
|
-
const
|
|
344798
|
+
const legacyQualified = requested.includes("/") ? this.config.allModels().filter(
|
|
344799
|
+
(model2) => `${model2.provider_id}/${model2.name}` === requested || `${model2.provider}/${model2.name}` === requested
|
|
344800
|
+
) : [];
|
|
344801
|
+
const current = qualified ? this.config.findDeployment(qualified) : legacyQualified.length === 1 ? legacyQualified[0] : requested ? this.config.findModel(requested) : void 0;
|
|
344668
344802
|
this.model = current?.name || requested;
|
|
344669
344803
|
this.fixedDeployment = current ? this.deploymentRef(current) : qualified;
|
|
344670
344804
|
this.resolvedDeployment = null;
|
|
@@ -345781,7 +345915,7 @@ ${String(event.toolArgs || "")}`;
|
|
|
345781
345915
|
this.saveWorkspaceConversationState(true);
|
|
345782
345916
|
return true;
|
|
345783
345917
|
}
|
|
345784
|
-
finishConversationWorkRun(runId, status, endedAt = this.nowIso()) {
|
|
345918
|
+
finishConversationWorkRun(runId, status, endedAt = this.nowIso(), errorMessage = "") {
|
|
345785
345919
|
const run = this.workRuns.find((item) => item.runId === String(runId || ""));
|
|
345786
345920
|
if (!run) return false;
|
|
345787
345921
|
this.syncAgentRunTerminal(run.runId, status, endedAt);
|
|
@@ -345825,7 +345959,7 @@ ${String(event.toolArgs || "")}`;
|
|
|
345825
345959
|
this.enforceGoalTerminalInvariant(status, goalAudit);
|
|
345826
345960
|
this.emitWorkEvent({
|
|
345827
345961
|
type: status === "completed" ? "done" : status === "error" ? "error" : "status",
|
|
345828
|
-
content: status === "force_interrupted" ? "Force interrupted." : status === "interrupted" ? "Interrupted." : "Response complete.",
|
|
345962
|
+
content: status === "error" ? String(errorMessage || "").trim() || "Agent run failed." : status === "force_interrupted" ? "Force interrupted." : status === "interrupted" ? "Interrupted." : "Response complete.",
|
|
345829
345963
|
status,
|
|
345830
345964
|
runId: run.runId,
|
|
345831
345965
|
conversationId: run.target.conversationId,
|
|
@@ -345970,6 +346104,7 @@ ${String(event.toolArgs || "")}`;
|
|
|
345970
346104
|
this.activeAgentKernelRuntime = runtime;
|
|
345971
346105
|
this.awaitingAgentKernelRuntime = false;
|
|
345972
346106
|
if (!runtime) return;
|
|
346107
|
+
if (this.activeProcessAbortController?.signal.aborted) runtime.abort?.();
|
|
345973
346108
|
const queued = this.pendingAgentKernelQueue.splice(0);
|
|
345974
346109
|
for (const item of queued) {
|
|
345975
346110
|
const accepted = this.forwardAgentKernelQueueMessage(item.content, item.queueMode, item.clientMessageId, item.runId, item.images, item.hiddenUserInput);
|
|
@@ -347131,6 +347266,25 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347131
347266
|
this.loadWorkspaceConversationState();
|
|
347132
347267
|
return selected;
|
|
347133
347268
|
}
|
|
347269
|
+
refreshWorkspaceRegistryFromStorage() {
|
|
347270
|
+
const before = JSON.stringify({
|
|
347271
|
+
internal: this.workspace.internal,
|
|
347272
|
+
external: this.workspace.external,
|
|
347273
|
+
current: this.workspace.current
|
|
347274
|
+
});
|
|
347275
|
+
const selected = this.workspace.reloadFromStorage();
|
|
347276
|
+
const after = JSON.stringify({
|
|
347277
|
+
internal: this.workspace.internal,
|
|
347278
|
+
external: this.workspace.external,
|
|
347279
|
+
current: this.workspace.current
|
|
347280
|
+
});
|
|
347281
|
+
if (before === after) return selected;
|
|
347282
|
+
if (selected) this.config.loadWorkspaceConfig(selected.path);
|
|
347283
|
+
else this.config.clearWorkspaceOverrides();
|
|
347284
|
+
this.workspaceConversations.clear();
|
|
347285
|
+
this.loadWorkspaceConversationState();
|
|
347286
|
+
return selected;
|
|
347287
|
+
}
|
|
347134
347288
|
setConversation(id) {
|
|
347135
347289
|
const clean = this.safeConversationId(id || "default");
|
|
347136
347290
|
if (this.workspaceConversationKey() === this.loadedWorkspaceConversationKey) {
|
|
@@ -348124,28 +348278,25 @@ ${summary}`, segment, "local-summarize", true);
|
|
|
348124
348278
|
this.saveWorkspaceConversationState(true);
|
|
348125
348279
|
return { text, hiddenUserInput: true, goalContinuation: true };
|
|
348126
348280
|
}
|
|
348127
|
-
|
|
348281
|
+
buildSessionArchive(messages, mode, model, archiveDir) {
|
|
348128
348282
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace("T", "_").replace("Z", "");
|
|
348129
|
-
const
|
|
348130
|
-
|
|
348131
|
-
const filename = `session_${stamp}.md`;
|
|
348132
|
-
const outPath = path28.join(archiveDir, filename);
|
|
348133
|
-
let md = `# Newmark Session \u2014 ${stamp}
|
|
348283
|
+
const filename = `session_${stamp}_${crypto14.randomUUID().slice(0, 8)}.md`;
|
|
348284
|
+
let markdown = `# Newmark Session \u2014 ${stamp}
|
|
348134
348285
|
|
|
348135
348286
|
`;
|
|
348136
|
-
|
|
348287
|
+
markdown += `**Mode**: ${mode}
|
|
348137
348288
|
**Model**: ${model}
|
|
348138
348289
|
`;
|
|
348139
|
-
|
|
348290
|
+
markdown += `**Messages**: ${messages.length}
|
|
348140
348291
|
|
|
348141
348292
|
---
|
|
348142
348293
|
|
|
348143
348294
|
`;
|
|
348144
|
-
if (this.goal)
|
|
348295
|
+
if (this.goal) markdown += `**Goal**: ${this.goal.objective}
|
|
348145
348296
|
|
|
348146
348297
|
`;
|
|
348147
348298
|
for (const msg of messages) {
|
|
348148
|
-
|
|
348299
|
+
markdown += `**[${msg.role}] ${msg.timestamp}**
|
|
348149
348300
|
|
|
348150
348301
|
${msg.content}
|
|
348151
348302
|
|
|
@@ -348154,13 +348305,35 @@ ${msg.content}
|
|
|
348154
348305
|
const archived = archiveConversationImageAttachment(this.rootPath, archiveDir, attachment);
|
|
348155
348306
|
if (!archived) continue;
|
|
348156
348307
|
const alt = archived.name.replace(/[\]\r\n]/g, " ").trim() || "Submitted image";
|
|
348157
|
-
|
|
348308
|
+
markdown += `
|
|
348158
348309
|
|
|
348159
348310
|
`;
|
|
348160
348311
|
}
|
|
348161
348312
|
}
|
|
348162
|
-
|
|
348163
|
-
|
|
348313
|
+
return { filename, markdown };
|
|
348314
|
+
}
|
|
348315
|
+
writeSessionArchive(messages, mode, model) {
|
|
348316
|
+
const archiveDir = this.archiveDir();
|
|
348317
|
+
fs25.mkdirSync(archiveDir, { recursive: true });
|
|
348318
|
+
const archive = this.buildSessionArchive(messages, mode, model, archiveDir);
|
|
348319
|
+
fs25.writeFileSync(path28.join(archiveDir, archive.filename), archive.markdown, "utf-8");
|
|
348320
|
+
return archive.filename;
|
|
348321
|
+
}
|
|
348322
|
+
async writeSessionArchiveAsync(messages, mode, model, archiveDir = this.archiveDir()) {
|
|
348323
|
+
const archive = this.buildSessionArchive(messages, mode, model, archiveDir);
|
|
348324
|
+
await fs25.promises.mkdir(archiveDir, { recursive: true });
|
|
348325
|
+
const outPath = path28.join(archiveDir, archive.filename);
|
|
348326
|
+
const tempPath = `${outPath}.${process.pid}.${crypto14.randomUUID()}.tmp`;
|
|
348327
|
+
try {
|
|
348328
|
+
await fs25.promises.writeFile(tempPath, archive.markdown, "utf-8");
|
|
348329
|
+
await fs25.promises.rename(tempPath, outPath);
|
|
348330
|
+
} finally {
|
|
348331
|
+
try {
|
|
348332
|
+
await fs25.promises.unlink(tempPath);
|
|
348333
|
+
} catch {
|
|
348334
|
+
}
|
|
348335
|
+
}
|
|
348336
|
+
return archive.filename;
|
|
348164
348337
|
}
|
|
348165
348338
|
archiveSession() {
|
|
348166
348339
|
return this.writeSessionArchive(this.chatMessages, this.modeName(), this.model);
|
|
@@ -348228,6 +348401,93 @@ ${msg.content}
|
|
|
348228
348401
|
}
|
|
348229
348402
|
return filename;
|
|
348230
348403
|
}
|
|
348404
|
+
/**
|
|
348405
|
+
* Non-blocking archive writer used by the desktop IPC path. The conversation
|
|
348406
|
+
* state merge remains synchronous and lock-protected, but the potentially
|
|
348407
|
+
* large markdown payload and manifest use promise-based filesystem I/O so
|
|
348408
|
+
* independent workspaces can archive in parallel without freezing Electron.
|
|
348409
|
+
*/
|
|
348410
|
+
async archiveConversationAsync(conversationId) {
|
|
348411
|
+
return await this.archiveConversationAsyncUnlocked(conversationId);
|
|
348412
|
+
}
|
|
348413
|
+
async archiveConversationAsyncUnlocked(conversationId) {
|
|
348414
|
+
const ws = this.workspace.current;
|
|
348415
|
+
if (!ws) return null;
|
|
348416
|
+
const clean = this.safeConversationId(conversationId || "default");
|
|
348417
|
+
const stateKey2 = this.workspaceConversationStateKey(clean);
|
|
348418
|
+
if (!stateKey2) return null;
|
|
348419
|
+
const memoryKey = `${ws.isInternal ? "internal" : "external"}:${path28.resolve(ws.path)}::conversation:${clean}`;
|
|
348420
|
+
const archiveDir = path28.join(ws.path, "archive");
|
|
348421
|
+
const workspacePrefix = this.workspaceConversationPrefix() || "";
|
|
348422
|
+
const archiveMode = this.modeName();
|
|
348423
|
+
const archiveModel = this.model;
|
|
348424
|
+
const cachedStored = this.readStoredConversationState(ws);
|
|
348425
|
+
const stored = JSON.parse(JSON.stringify(cachedStored || {}));
|
|
348426
|
+
const persisted = stored.conversations?.[stateKey2];
|
|
348427
|
+
if (persisted) this.normalizeConversationTree(persisted);
|
|
348428
|
+
const memory = this.workspaceConversations.get(memoryKey);
|
|
348429
|
+
const persistedMessagesAvailable = persisted?.chatMessages !== void 0;
|
|
348430
|
+
const sourceMessages = persisted?.chatMessages ?? memory?.chatMessages ?? [];
|
|
348431
|
+
const sourceHistory = persistedMessagesAvailable ? persisted?.history ?? [] : memory?.history ?? persisted?.history ?? [];
|
|
348432
|
+
const messages = this.normalizeConversationChatMessages(sourceMessages, sourceHistory);
|
|
348433
|
+
const filename = await this.writeSessionArchiveAsync(messages, archiveMode, archiveModel, archiveDir);
|
|
348434
|
+
const archiveEntry = persisted ? JSON.parse(JSON.stringify(persisted)) : {
|
|
348435
|
+
title: this.titleFromMessages(messages, clean),
|
|
348436
|
+
chatMessages: messages,
|
|
348437
|
+
history: sourceHistory,
|
|
348438
|
+
plan: memory?.plan,
|
|
348439
|
+
linkedPlan: memory?.linkedPlan,
|
|
348440
|
+
subagentState: memory?.subagentState,
|
|
348441
|
+
workRuns: memory?.workRuns,
|
|
348442
|
+
continuations: memory?.continuations,
|
|
348443
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
348444
|
+
};
|
|
348445
|
+
const manifest = {
|
|
348446
|
+
version: 2,
|
|
348447
|
+
kind: "newmark-conversation-archive",
|
|
348448
|
+
archivedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
348449
|
+
conversationId: clean,
|
|
348450
|
+
workspaceId: ws.id,
|
|
348451
|
+
workspaceName: ws.name,
|
|
348452
|
+
workspacePath: ws.path,
|
|
348453
|
+
workspaceInternal: ws.isInternal,
|
|
348454
|
+
statePrefix: workspacePrefix,
|
|
348455
|
+
entry: this.conversationEntryForDisk(archiveEntry)
|
|
348456
|
+
};
|
|
348457
|
+
const manifestPath = this.archiveManifestPath(path28.join(archiveDir, filename));
|
|
348458
|
+
const manifestTempPath = `${manifestPath}.${process.pid}.${crypto14.randomUUID()}.tmp`;
|
|
348459
|
+
try {
|
|
348460
|
+
await fs25.promises.writeFile(manifestTempPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
348461
|
+
await fs25.promises.rename(manifestTempPath, manifestPath);
|
|
348462
|
+
} finally {
|
|
348463
|
+
try {
|
|
348464
|
+
await fs25.promises.unlink(manifestTempPath);
|
|
348465
|
+
} catch {
|
|
348466
|
+
}
|
|
348467
|
+
}
|
|
348468
|
+
this.finalizeAsyncConversationArchive(clean, stateKey2, memoryKey, ws);
|
|
348469
|
+
return filename;
|
|
348470
|
+
}
|
|
348471
|
+
finalizeAsyncConversationArchive(clean, stateKey2, memoryKey, ws) {
|
|
348472
|
+
let nextActiveId = "";
|
|
348473
|
+
this.mutateStoredConversationState(ws, (latest) => {
|
|
348474
|
+
latest.conversations = latest.conversations || {};
|
|
348475
|
+
delete latest.conversations[stateKey2];
|
|
348476
|
+
const prefix = stateKey2.slice(0, Math.max(0, stateKey2.length - clean.length - 1)) + "-";
|
|
348477
|
+
const remaining = Object.keys(latest.conversations).filter((key3) => !prefix || key3.startsWith(prefix)).map((key3) => key3.slice(prefix.length)).filter(Boolean);
|
|
348478
|
+
const currentActiveId = this.safeConversationId(latest.activeConversationId || this.activeConversationId || "default");
|
|
348479
|
+
if (clean === currentActiveId) latest.activeConversationId = remaining[0] || "default";
|
|
348480
|
+
nextActiveId = latest.activeConversationId || remaining[0] || "default";
|
|
348481
|
+
return latest;
|
|
348482
|
+
});
|
|
348483
|
+
this.workspaceConversations.delete(memoryKey);
|
|
348484
|
+
const duplicateMemoryKey = `${ws.isInternal ? "internal" : "external"}:${path28.resolve(ws.path)}::conversation:${clean}`;
|
|
348485
|
+
this.workspaceConversations.delete(duplicateMemoryKey);
|
|
348486
|
+
if (clean === this.safeConversationId(this.activeConversationId || "default")) {
|
|
348487
|
+
this.activeConversationId = nextActiveId || "default";
|
|
348488
|
+
this.loadWorkspaceConversationState();
|
|
348489
|
+
}
|
|
348490
|
+
}
|
|
348231
348491
|
listStoredConversationIds(stored) {
|
|
348232
348492
|
const prefix = `${this.workspaceConversationPrefix() || ""}-`;
|
|
348233
348493
|
return Object.keys(stored.conversations || {}).filter((key3) => !prefix || key3.startsWith(prefix)).map((key3) => key3.slice(prefix.length)).filter(Boolean);
|
|
@@ -348753,9 +349013,9 @@ ${msg.content}
|
|
|
348753
349013
|
const provider = this.config.findProvider(providerId);
|
|
348754
349014
|
return all.filter((m2) => m2.provider_id === (provider?.id || providerId));
|
|
348755
349015
|
}
|
|
348756
|
-
async validateModels(selectedNames) {
|
|
349016
|
+
async validateModels(selectedNames, options = {}) {
|
|
348757
349017
|
if (this.modelValidationPromise) return this.modelValidationPromise;
|
|
348758
|
-
const validation = this.runModelValidation(selectedNames);
|
|
349018
|
+
const validation = this.runModelValidation(selectedNames, options.persist !== false);
|
|
348759
349019
|
this.modelValidationPromise = validation;
|
|
348760
349020
|
try {
|
|
348761
349021
|
return await validation;
|
|
@@ -348773,7 +349033,7 @@ ${msg.content}
|
|
|
348773
349033
|
recentChecks: this.modelValidationProgress.recentChecks.map((item) => ({ ...item }))
|
|
348774
349034
|
};
|
|
348775
349035
|
}
|
|
348776
|
-
async runModelValidation(selectedNames) {
|
|
349036
|
+
async runModelValidation(selectedNames, persist = true) {
|
|
348777
349037
|
const selectedModels = this.config.modelsForSelections(selectedNames);
|
|
348778
349038
|
if (!selectedModels.length) {
|
|
348779
349039
|
this.modelValidationProgress = {
|
|
@@ -348791,7 +349051,7 @@ ${msg.content}
|
|
|
348791
349051
|
}
|
|
348792
349052
|
const results = [];
|
|
348793
349053
|
const catalogByProvider = /* @__PURE__ */ new Map();
|
|
348794
|
-
const cache = new FileModelValidationCache(this.rootPath);
|
|
349054
|
+
const cache = new FileModelValidationCache(this.rootPath, { readOnly: !persist });
|
|
348795
349055
|
const checksPerModel = 11;
|
|
348796
349056
|
let currentModel = "";
|
|
348797
349057
|
let currentModelChecks = 0;
|
|
@@ -348923,7 +349183,7 @@ ${msg.content}
|
|
|
348923
349183
|
completedModels: this.modelValidationProgress.completedModels + 1
|
|
348924
349184
|
};
|
|
348925
349185
|
}
|
|
348926
|
-
this.config.save();
|
|
349186
|
+
if (persist) this.config.save();
|
|
348927
349187
|
this.modelValidationProgress = {
|
|
348928
349188
|
...this.modelValidationProgress,
|
|
348929
349189
|
running: false,
|
|
@@ -349175,7 +349435,8 @@ ${String(input2.content || "").slice(0, 18e3)}`;
|
|
|
349175
349435
|
const text = typeof input2 === "string" ? input2 : String(input2.text || "");
|
|
349176
349436
|
const inputEnvelope = typeof input2 === "string" ? null : input2;
|
|
349177
349437
|
const hiddenUserInput = inputEnvelope?.hiddenUserInput === true;
|
|
349178
|
-
this.
|
|
349438
|
+
const explicitFixedModel = this.model !== "" && this.model !== "auto";
|
|
349439
|
+
if (!explicitFixedModel) this.ensureUsableModelSelection();
|
|
349179
349440
|
const clientMessageId = String(inputEnvelope?.clientMessageId || "").trim();
|
|
349180
349441
|
const inputRunId = String(inputEnvelope?.runId || this.activeWorkRunId || "").trim();
|
|
349181
349442
|
const rawImages = typeof input2 === "string" ? [] : Array.isArray(input2.images) ? input2.images : [];
|
|
@@ -349263,7 +349524,13 @@ ${String(input2.content || "").slice(0, 18e3)}`;
|
|
|
349263
349524
|
await this.evaluateAndSwitch(displayText, inputEnvelope?.routePolicy);
|
|
349264
349525
|
}
|
|
349265
349526
|
if (this.model && this.modelIsUnavailable(this.model)) {
|
|
349527
|
+
const requestedModel = this.model;
|
|
349266
349528
|
this.switchToFallbackModel();
|
|
349529
|
+
if (this.modelIsUnavailable(this.model)) {
|
|
349530
|
+
const message = `[Error] Model '${requestedModel || "unknown"}' is unavailable or not configured. Select a configured model or enable a valid provider before sending.`;
|
|
349531
|
+
this.status = "error";
|
|
349532
|
+
throw new Error(message);
|
|
349533
|
+
}
|
|
349267
349534
|
}
|
|
349268
349535
|
if (this.engine === "opencode") {
|
|
349269
349536
|
if (images.length) return [{ type: "text", text: "[Vision unavailable] The OpenCode engine does not accept Newmark image attachments." }];
|
|
@@ -350256,11 +350523,11 @@ Falling back to built-in engine.` }];
|
|
|
350256
350523
|
}
|
|
350257
350524
|
}
|
|
350258
350525
|
async maybeCompress(msgs, provider, signal, compressionModel, force = false) {
|
|
350259
|
-
if (signal?.aborted) return;
|
|
350260
|
-
if (!this.config.getBool("context", "auto_compress")) return;
|
|
350526
|
+
if (signal?.aborted) return false;
|
|
350527
|
+
if (!this.config.getBool("context", "auto_compress")) return false;
|
|
350261
350528
|
const total = msgs.reduce((sum, m2) => sum + (typeof m2.content === "string" ? m2.content.length : JSON.stringify(m2.content || "").length), 0);
|
|
350262
350529
|
const budget = this.compressionBudget(msgs);
|
|
350263
|
-
if (budget.estimatedTokens < budget.triggerTokens && !force) return;
|
|
350530
|
+
if (budget.estimatedTokens < budget.triggerTokens && !force) return false;
|
|
350264
350531
|
if (!force && this.lastCompression && String(msgs[0]?.content || "").includes(this.lastCompression.summary)) {
|
|
350265
350532
|
const baselineChars = Math.max(0, Number(this.lastCompression.compressedChars || 0));
|
|
350266
350533
|
const baselineTokens = Math.max(0, Number(this.lastCompression.compressedTokens || 0));
|
|
@@ -350268,16 +350535,16 @@ Falling back to built-in engine.` }];
|
|
|
350268
350535
|
const tokenGrowth = baselineTokens ? Math.max(0, budget.estimatedTokens - baselineTokens) : Number.POSITIVE_INFINITY;
|
|
350269
350536
|
const minCharGrowth = Math.max(12e3, Math.floor(baselineChars * 0.25));
|
|
350270
350537
|
const minTokenGrowth = Math.max(1024, Math.floor(budget.triggerTokens * 0.2));
|
|
350271
|
-
if (charGrowth < minCharGrowth && tokenGrowth < minTokenGrowth) return;
|
|
350538
|
+
if (charGrowth < minCharGrowth && tokenGrowth < minTokenGrowth) return false;
|
|
350272
350539
|
}
|
|
350273
350540
|
const originalMessageCount = msgs.length;
|
|
350274
350541
|
const configuredKeepLast = this.config.getNum("context", "keep_recent_messages") || 10;
|
|
350275
|
-
if (msgs.length <= 1) return;
|
|
350542
|
+
if (msgs.length <= 1) return false;
|
|
350276
350543
|
const continuationAnchorTokens = this.estimateContextTokens([this.postCompressionContinuationMessage()]);
|
|
350277
350544
|
const recentBudget = Math.max(64, budget.targetTokens - budget.summaryTokens - continuationAnchorTokens);
|
|
350278
350545
|
const recent = this.recentContextSuffix(msgs, configuredKeepLast, recentBudget);
|
|
350279
350546
|
const recentStart = Math.max(0, msgs.length - recent.length);
|
|
350280
|
-
if (recentStart <= 0) return;
|
|
350547
|
+
if (recentStart <= 0) return false;
|
|
350281
350548
|
const middle = msgs.slice(0, recentStart);
|
|
350282
350549
|
const currentInstruction = this.latestUserHistoryText(recent);
|
|
350283
350550
|
const compression = await this.buildCompressionSummary(
|
|
@@ -350289,7 +350556,7 @@ Falling back to built-in engine.` }];
|
|
|
350289
350556
|
compressionModel || this.activeModelName(),
|
|
350290
350557
|
currentInstruction
|
|
350291
350558
|
);
|
|
350292
|
-
if (signal?.aborted) return;
|
|
350559
|
+
if (signal?.aborted) return false;
|
|
350293
350560
|
const compressed = [{
|
|
350294
350561
|
role: "system",
|
|
350295
350562
|
content: compression.summary
|
|
@@ -350314,6 +350581,7 @@ Falling back to built-in engine.` }];
|
|
|
350314
350581
|
};
|
|
350315
350582
|
this.pushCompressionCacheEntry(compression.summary, middle, compression.model, compression.fallback);
|
|
350316
350583
|
this.persistCompressedHistory(compression.summary, recent.length, msgs);
|
|
350584
|
+
return true;
|
|
350317
350585
|
}
|
|
350318
350586
|
async buildCompressionSummary(middle, totalChars, budget, provider, signal, compressionModel, currentInstruction = "") {
|
|
350319
350587
|
const workspacePath = this.workspace.current?.path || this.rootPath;
|
|
@@ -351649,7 +351917,12 @@ var ConversationKernel = class {
|
|
|
351649
351917
|
if (runtime.runId === runId && runtime.stopRequestedRunId === runId) {
|
|
351650
351918
|
stopped = true;
|
|
351651
351919
|
} else {
|
|
351652
|
-
runtime.runner.finishConversationWorkRun(
|
|
351920
|
+
runtime.runner.finishConversationWorkRun(
|
|
351921
|
+
runId,
|
|
351922
|
+
"error",
|
|
351923
|
+
void 0,
|
|
351924
|
+
error instanceof Error ? error.message : String(error)
|
|
351925
|
+
);
|
|
351653
351926
|
throw error;
|
|
351654
351927
|
}
|
|
351655
351928
|
} finally {
|