newmark-agent 0.4.9 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/conversation-utility-host.bundle.cjs +118 -11
- package/dist/core/conversationKernel.d.ts +2 -0
- package/dist/core/conversationKernel.js +68 -10
- package/dist/core/workEventCoalescer.d.ts +16 -0
- package/dist/core/workEventCoalescer.js +52 -0
- package/dist/llm/provider.d.ts +5 -0
- package/dist/llm/provider.js +63 -1
- package/dist/main.js +21 -2
- package/dist/server.d.ts +1 -0
- package/dist/server.js +181 -3
- package/dist/ui/index.html +145 -37
- package/dist/wsl-agent-host.bundle.cjs +118 -11
- package/package.json +6 -4
|
@@ -329452,6 +329452,7 @@ var LLMProvider = class _LLMProvider {
|
|
|
329452
329452
|
thinkingTierMaps;
|
|
329453
329453
|
static nodeHttpTransport = null;
|
|
329454
329454
|
static powershellTransport = null;
|
|
329455
|
+
temperatureUnsupported = /* @__PURE__ */ new Set();
|
|
329455
329456
|
effectiveRequestTimeout(timeoutMs) {
|
|
329456
329457
|
const requested = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS;
|
|
329457
329458
|
const configured = Number.isFinite(this.requestTimeoutMs) && this.requestTimeoutMs > 0 ? this.requestTimeoutMs : DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS;
|
|
@@ -329593,7 +329594,46 @@ var LLMProvider = class _LLMProvider {
|
|
|
329593
329594
|
"X-GitHub-Api-Version": "2022-11-28"
|
|
329594
329595
|
};
|
|
329595
329596
|
}
|
|
329597
|
+
temperatureCapabilityKey(url, body) {
|
|
329598
|
+
return `${url}|${String(body.model || "")}`;
|
|
329599
|
+
}
|
|
329600
|
+
unsupportedTemperatureError(status, raw) {
|
|
329601
|
+
if (status !== 400) return false;
|
|
329602
|
+
try {
|
|
329603
|
+
const parsed = JSON.parse(String(raw || ""));
|
|
329604
|
+
if (String(parsed?.error?.param || "").toLowerCase() === "temperature") return true;
|
|
329605
|
+
return /unsupported parameter[^\n]*temperature|temperature[^\n]*not supported/i.test(String(parsed?.error?.message || ""));
|
|
329606
|
+
} catch {
|
|
329607
|
+
return /unsupported parameter[^\n]*temperature|temperature[^\n]*not supported/i.test(String(raw || ""));
|
|
329608
|
+
}
|
|
329609
|
+
}
|
|
329610
|
+
requestBodyForTemperatureCapability(url, body) {
|
|
329611
|
+
const prepared = { ...body };
|
|
329612
|
+
if (this.temperatureUnsupported.has(this.temperatureCapabilityKey(url, body))) delete prepared.temperature;
|
|
329613
|
+
return prepared;
|
|
329614
|
+
}
|
|
329596
329615
|
async postJsonWithFetchFallback(url, headers, body, timeoutMs = 12e4, signal) {
|
|
329616
|
+
const prepared = this.requestBodyForTemperatureCapability(url, body);
|
|
329617
|
+
const response = await this.postJsonOnce(url, headers, prepared, timeoutMs, signal);
|
|
329618
|
+
if (prepared.temperature === void 0 || response.status !== 400) return response;
|
|
329619
|
+
const cloneable = typeof response.clone === "function";
|
|
329620
|
+
const errorText = await (cloneable ? response.clone() : response).text();
|
|
329621
|
+
if (!this.unsupportedTemperatureError(response.status, errorText)) {
|
|
329622
|
+
if (cloneable) return response;
|
|
329623
|
+
return {
|
|
329624
|
+
ok: response.ok,
|
|
329625
|
+
status: response.status,
|
|
329626
|
+
headers: response.headers,
|
|
329627
|
+
text: async () => errorText,
|
|
329628
|
+
json: async () => JSON.parse(errorText || "{}")
|
|
329629
|
+
};
|
|
329630
|
+
}
|
|
329631
|
+
this.temperatureUnsupported.add(this.temperatureCapabilityKey(url, body));
|
|
329632
|
+
const retryBody = { ...body };
|
|
329633
|
+
delete retryBody.temperature;
|
|
329634
|
+
return this.postJsonOnce(url, headers, retryBody, timeoutMs, signal);
|
|
329635
|
+
}
|
|
329636
|
+
async postJsonOnce(url, headers, body, timeoutMs = 12e4, signal) {
|
|
329597
329637
|
const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
|
|
329598
329638
|
if (this.isPlainHttpLoopback(url)) {
|
|
329599
329639
|
const pathname = (() => {
|
|
@@ -330262,6 +330302,7 @@ ${responsePath}
|
|
|
330262
330302
|
*/
|
|
330263
330303
|
buildProviderAdapterTransport() {
|
|
330264
330304
|
return async (request, signal) => {
|
|
330305
|
+
request.body = this.requestBodyForTemperatureCapability(request.url, request.body);
|
|
330265
330306
|
if (request.body?.stream === true) {
|
|
330266
330307
|
const abort = new AbortController();
|
|
330267
330308
|
const forwardAbort = () => abort.abort(signal?.reason);
|
|
@@ -330271,12 +330312,27 @@ ${responsePath}
|
|
|
330271
330312
|
const timer = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
|
|
330272
330313
|
try {
|
|
330273
330314
|
try {
|
|
330274
|
-
|
|
330315
|
+
let response2 = await fetch(request.url, {
|
|
330275
330316
|
method: "POST",
|
|
330276
330317
|
headers: request.headers,
|
|
330277
330318
|
body: JSON.stringify(request.body),
|
|
330278
330319
|
signal: abort.signal
|
|
330279
330320
|
});
|
|
330321
|
+
if (request.body.temperature !== void 0 && response2.status === 400) {
|
|
330322
|
+
const errorText = await response2.clone().text();
|
|
330323
|
+
if (this.unsupportedTemperatureError(response2.status, errorText)) {
|
|
330324
|
+
this.temperatureUnsupported.add(this.temperatureCapabilityKey(request.url, request.body));
|
|
330325
|
+
const retryBody = { ...request.body };
|
|
330326
|
+
delete retryBody.temperature;
|
|
330327
|
+
response2 = await fetch(request.url, {
|
|
330328
|
+
method: "POST",
|
|
330329
|
+
headers: request.headers,
|
|
330330
|
+
body: JSON.stringify(retryBody),
|
|
330331
|
+
signal: abort.signal
|
|
330332
|
+
});
|
|
330333
|
+
}
|
|
330334
|
+
}
|
|
330335
|
+
return response2;
|
|
330280
330336
|
} catch (error) {
|
|
330281
330337
|
if (signal?.aborted) throw abortFailure(signal);
|
|
330282
330338
|
if (abort.signal.aborted) throw abortFailure(abort.signal);
|
|
@@ -353571,7 +353627,7 @@ ${item.goalObjective}` : item.text,
|
|
|
353571
353627
|
if (!clientMessageId) return { ...base2, reason: "clientMessageId is required" };
|
|
353572
353628
|
const existing = runtime ? this.guideReceipt(runtime, clientMessageId) : void 0;
|
|
353573
353629
|
if (existing) return existing;
|
|
353574
|
-
if (!runtime?.
|
|
353630
|
+
if (!runtime?.runId) {
|
|
353575
353631
|
return { ...base2, reason: "Target conversation is not running" };
|
|
353576
353632
|
}
|
|
353577
353633
|
if (requestedRunId && requestedRunId !== runtime.runId) {
|
|
@@ -353579,6 +353635,10 @@ ${item.goalObjective}` : item.text,
|
|
|
353579
353635
|
runtime.guideReceipts.set(clientMessageId, rejected);
|
|
353580
353636
|
return runtime.runner.recordGuideReceipt(rejected);
|
|
353581
353637
|
}
|
|
353638
|
+
const canReactivateFinalizingRun = !runtime.activePromise && runtime.guideAcceptanceClosedRunId === runtime.runId && (!requestedRunId || requestedRunId === runtime.runId);
|
|
353639
|
+
if (!runtime.activePromise && !canReactivateFinalizingRun) {
|
|
353640
|
+
return { ...base2, reason: "Target conversation is not running" };
|
|
353641
|
+
}
|
|
353582
353642
|
let safeImages = [];
|
|
353583
353643
|
let safeAttachments = [];
|
|
353584
353644
|
try {
|
|
@@ -353673,6 +353733,8 @@ ${item.goalObjective}` : item.text,
|
|
|
353673
353733
|
attachments: safeAttachments.map((attachment) => ({ ...attachment })),
|
|
353674
353734
|
createdAt: deferred2.createdAt
|
|
353675
353735
|
}]);
|
|
353736
|
+
this.trackQueuedMessage(runtime, safeEnvelope.text, "steer");
|
|
353737
|
+
this.emitQueueUpdate(runtime);
|
|
353676
353738
|
this.activateAcceptedGoal(runtime, envelope.goalObjective);
|
|
353677
353739
|
this.schedulePendingRuntimeContinuation(runtime, runtime.runId);
|
|
353678
353740
|
return deferred2;
|
|
@@ -353937,6 +353999,7 @@ ${item.goalObjective}` : item.text,
|
|
|
353937
353999
|
activePromise = (async () => {
|
|
353938
354000
|
let result = null;
|
|
353939
354001
|
let stopped = false;
|
|
354002
|
+
let failed = false;
|
|
353940
354003
|
try {
|
|
353941
354004
|
result = await this.run(runtime, message, options);
|
|
353942
354005
|
stopped = runtime.runId === runId && runtime.stopRequestedRunId === runId;
|
|
@@ -353944,6 +354007,8 @@ ${item.goalObjective}` : item.text,
|
|
|
353944
354007
|
if (runtime.runId === runId && runtime.stopRequestedRunId === runId) {
|
|
353945
354008
|
stopped = true;
|
|
353946
354009
|
} else {
|
|
354010
|
+
failed = true;
|
|
354011
|
+
this.stopAutomaticContinuationAfterError(runtime, runId);
|
|
353947
354012
|
runtime.runner.finishConversationWorkRun(
|
|
353948
354013
|
runId,
|
|
353949
354014
|
"error",
|
|
@@ -353958,12 +354023,12 @@ ${item.goalObjective}` : item.text,
|
|
|
353958
354023
|
if (runtime.stopRequestedRunId === runId) {
|
|
353959
354024
|
stopped = true;
|
|
353960
354025
|
this.settleCooperativeStop(runtime, runId);
|
|
353961
|
-
} else if (!runtime.queuePaused && runtime.pendingNextTurn.length > 0) {
|
|
354026
|
+
} else if (!failed && !runtime.queuePaused && runtime.pendingNextTurn.length > 0) {
|
|
353962
354027
|
this.schedulePendingRuntimeContinuation(runtime, runId);
|
|
353963
354028
|
}
|
|
353964
354029
|
}
|
|
353965
354030
|
}
|
|
353966
|
-
if (!stopped && runtime.runId === runId) this.scheduleGoalContinuation(runtime, runId);
|
|
354031
|
+
if (!failed && !stopped && runtime.runId === runId) this.scheduleGoalContinuation(runtime, runId);
|
|
353967
354032
|
if (stopped) {
|
|
353968
354033
|
const settled = this.result(runtime, []);
|
|
353969
354034
|
if (result?.tokens) settled.tokens = result.tokens;
|
|
@@ -353985,6 +354050,28 @@ ${item.goalObjective}` : item.text,
|
|
|
353985
354050
|
this.emitQueueUpdate(runtime);
|
|
353986
354051
|
return true;
|
|
353987
354052
|
}
|
|
354053
|
+
stopAutomaticContinuationAfterError(runtime, runId) {
|
|
354054
|
+
if (runtime.runId !== runId) return;
|
|
354055
|
+
if (runtime.goalContinuationTimer) {
|
|
354056
|
+
clearTimeout(runtime.goalContinuationTimer);
|
|
354057
|
+
runtime.goalContinuationTimer = void 0;
|
|
354058
|
+
}
|
|
354059
|
+
runtime.pendingContinuationRunId = void 0;
|
|
354060
|
+
this.rejectOutstandingGuides(runtime, "The provider failed before this Guide could be applied; submit again to retry.");
|
|
354061
|
+
runtime.pendingNextTurn = runtime.pendingNextTurn.filter((item) => {
|
|
354062
|
+
const automatic = item.queueMode === "steer" || typeof item.message !== "string" && item.message.hiddenUserInput === true;
|
|
354063
|
+
if (automatic) {
|
|
354064
|
+
runtime.runner.consumeConversationContinuation({
|
|
354065
|
+
content: typeof item.message === "string" ? item.message : item.message.text,
|
|
354066
|
+
queueMode: item.queueMode,
|
|
354067
|
+
clientMessageId: typeof item.message === "string" ? void 0 : item.message.clientMessageId
|
|
354068
|
+
});
|
|
354069
|
+
}
|
|
354070
|
+
return !automatic;
|
|
354071
|
+
});
|
|
354072
|
+
this.queueState(runtime);
|
|
354073
|
+
this.emitQueueUpdate(runtime);
|
|
354074
|
+
}
|
|
353988
354075
|
async run(runtime, message, options) {
|
|
353989
354076
|
this.applyOptions(runtime.runner, options);
|
|
353990
354077
|
let lastTokens = await this.runSingle(runtime, message);
|
|
@@ -354078,7 +354165,13 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354078
354165
|
this.consumeQueuedMessage(runtime, typeof message === "string" ? message : message.text);
|
|
354079
354166
|
const timeoutMs = this.processTimeoutMs(runtime);
|
|
354080
354167
|
if (timeoutMs <= 0) {
|
|
354081
|
-
|
|
354168
|
+
let tokens;
|
|
354169
|
+
try {
|
|
354170
|
+
tokens = await runtime.runner.process(message);
|
|
354171
|
+
} catch (error) {
|
|
354172
|
+
this.consumeFailedAutomaticContinuation(runtime, message, continuationMode);
|
|
354173
|
+
throw error;
|
|
354174
|
+
}
|
|
354082
354175
|
if (continuationMode) runtime.runner.consumeConversationContinuation({
|
|
354083
354176
|
content: typeof message === "string" ? message : message.text,
|
|
354084
354177
|
queueMode: continuationMode,
|
|
@@ -354088,12 +354181,18 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354088
354181
|
}
|
|
354089
354182
|
let timeout;
|
|
354090
354183
|
try {
|
|
354091
|
-
|
|
354092
|
-
|
|
354093
|
-
|
|
354094
|
-
|
|
354095
|
-
|
|
354096
|
-
|
|
354184
|
+
let tokens;
|
|
354185
|
+
try {
|
|
354186
|
+
tokens = await Promise.race([
|
|
354187
|
+
runtime.runner.process(message),
|
|
354188
|
+
new Promise((_3, reject) => {
|
|
354189
|
+
timeout = setTimeout(() => reject(new Error(`Process timeout (${Math.round(timeoutMs / 1e3)}s)`)), timeoutMs);
|
|
354190
|
+
})
|
|
354191
|
+
]);
|
|
354192
|
+
} catch (error) {
|
|
354193
|
+
this.consumeFailedAutomaticContinuation(runtime, message, continuationMode);
|
|
354194
|
+
throw error;
|
|
354195
|
+
}
|
|
354097
354196
|
if (continuationMode) runtime.runner.consumeConversationContinuation({
|
|
354098
354197
|
content: typeof message === "string" ? message : message.text,
|
|
354099
354198
|
queueMode: continuationMode,
|
|
@@ -354104,6 +354203,14 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
354104
354203
|
if (timeout) clearTimeout(timeout);
|
|
354105
354204
|
}
|
|
354106
354205
|
}
|
|
354206
|
+
consumeFailedAutomaticContinuation(runtime, message, continuationMode) {
|
|
354207
|
+
if (!continuationMode || typeof message === "string" || message.hiddenUserInput !== true) return;
|
|
354208
|
+
runtime.runner.consumeConversationContinuation({
|
|
354209
|
+
content: message.text,
|
|
354210
|
+
queueMode: continuationMode,
|
|
354211
|
+
clientMessageId: message.clientMessageId
|
|
354212
|
+
});
|
|
354213
|
+
}
|
|
354107
354214
|
processTimeoutMs(runtime) {
|
|
354108
354215
|
const raw = runtime.runner.config.getNum("agent", "process_timeout_ms") || this.host.config.getNum("agent", "process_timeout_ms");
|
|
354109
354216
|
if (!Number.isFinite(raw) || raw <= 0) return 0;
|
|
@@ -271,6 +271,7 @@ export declare class ConversationKernel {
|
|
|
271
271
|
rewind(target: ConversationTargetInput, messageIndex: number): ReturnType<Agent['rewindConversation']>;
|
|
272
272
|
prompt(message: string | AgentPromptMessage, target: ConversationTargetInput, options: ConversationKernelRunOptions, queueMode?: ConversationQueueMode): Promise<ConversationKernelRunResult>;
|
|
273
273
|
private settleCooperativeStop;
|
|
274
|
+
private stopAutomaticContinuationAfterError;
|
|
274
275
|
private run;
|
|
275
276
|
/**
|
|
276
277
|
* Apply a model selection recorded while a Build block was running. The
|
|
@@ -280,6 +281,7 @@ export declare class ConversationKernel {
|
|
|
280
281
|
*/
|
|
281
282
|
private syncPendingModel;
|
|
282
283
|
private runSingle;
|
|
284
|
+
private consumeFailedAutomaticContinuation;
|
|
283
285
|
private processTimeoutMs;
|
|
284
286
|
private runtime;
|
|
285
287
|
private scheduleGoalContinuation;
|
|
@@ -353,7 +353,7 @@ class ConversationKernel {
|
|
|
353
353
|
const existing = runtime ? this.guideReceipt(runtime, clientMessageId) : undefined;
|
|
354
354
|
if (existing)
|
|
355
355
|
return existing;
|
|
356
|
-
if (!runtime?.
|
|
356
|
+
if (!runtime?.runId) {
|
|
357
357
|
return { ...base, reason: 'Target conversation is not running' };
|
|
358
358
|
}
|
|
359
359
|
if (requestedRunId && requestedRunId !== runtime.runId) {
|
|
@@ -361,6 +361,12 @@ class ConversationKernel {
|
|
|
361
361
|
runtime.guideReceipts.set(clientMessageId, rejected);
|
|
362
362
|
return runtime.runner.recordGuideReceipt(rejected);
|
|
363
363
|
}
|
|
364
|
+
const canReactivateFinalizingRun = !runtime.activePromise
|
|
365
|
+
&& runtime.guideAcceptanceClosedRunId === runtime.runId
|
|
366
|
+
&& (!requestedRunId || requestedRunId === runtime.runId);
|
|
367
|
+
if (!runtime.activePromise && !canReactivateFinalizingRun) {
|
|
368
|
+
return { ...base, reason: 'Target conversation is not running' };
|
|
369
|
+
}
|
|
364
370
|
let safeImages = [];
|
|
365
371
|
let safeAttachments = [];
|
|
366
372
|
try {
|
|
@@ -460,6 +466,8 @@ class ConversationKernel {
|
|
|
460
466
|
attachments: safeAttachments.map(attachment => ({ ...attachment })),
|
|
461
467
|
createdAt: deferred.createdAt,
|
|
462
468
|
}]);
|
|
469
|
+
this.trackQueuedMessage(runtime, safeEnvelope.text, 'steer');
|
|
470
|
+
this.emitQueueUpdate(runtime);
|
|
463
471
|
this.activateAcceptedGoal(runtime, envelope.goalObjective);
|
|
464
472
|
this.schedulePendingRuntimeContinuation(runtime, runtime.runId);
|
|
465
473
|
return deferred;
|
|
@@ -754,6 +762,7 @@ class ConversationKernel {
|
|
|
754
762
|
activePromise = (async () => {
|
|
755
763
|
let result = null;
|
|
756
764
|
let stopped = false;
|
|
765
|
+
let failed = false;
|
|
757
766
|
try {
|
|
758
767
|
result = await this.run(runtime, message, options);
|
|
759
768
|
stopped = runtime.runId === runId && runtime.stopRequestedRunId === runId;
|
|
@@ -763,6 +772,8 @@ class ConversationKernel {
|
|
|
763
772
|
stopped = true;
|
|
764
773
|
}
|
|
765
774
|
else {
|
|
775
|
+
failed = true;
|
|
776
|
+
this.stopAutomaticContinuationAfterError(runtime, runId);
|
|
766
777
|
runtime.runner.finishConversationWorkRun(runId, 'error', undefined, error instanceof Error ? error.message : String(error));
|
|
767
778
|
throw error;
|
|
768
779
|
}
|
|
@@ -774,7 +785,7 @@ class ConversationKernel {
|
|
|
774
785
|
stopped = true;
|
|
775
786
|
this.settleCooperativeStop(runtime, runId);
|
|
776
787
|
}
|
|
777
|
-
else if (!runtime.queuePaused && runtime.pendingNextTurn.length > 0) {
|
|
788
|
+
else if (!failed && !runtime.queuePaused && runtime.pendingNextTurn.length > 0) {
|
|
778
789
|
// A renderer/IPC Guide can arrive after the final-drain barrier's
|
|
779
790
|
// last check but before this promise settles. Do not leave the
|
|
780
791
|
// deferred continuation queued on an idle runtime.
|
|
@@ -782,7 +793,7 @@ class ConversationKernel {
|
|
|
782
793
|
}
|
|
783
794
|
}
|
|
784
795
|
}
|
|
785
|
-
if (!stopped && runtime.runId === runId)
|
|
796
|
+
if (!failed && !stopped && runtime.runId === runId)
|
|
786
797
|
this.scheduleGoalContinuation(runtime, runId);
|
|
787
798
|
if (stopped) {
|
|
788
799
|
const settled = this.result(runtime, []);
|
|
@@ -809,6 +820,30 @@ class ConversationKernel {
|
|
|
809
820
|
this.emitQueueUpdate(runtime);
|
|
810
821
|
return true;
|
|
811
822
|
}
|
|
823
|
+
stopAutomaticContinuationAfterError(runtime, runId) {
|
|
824
|
+
if (runtime.runId !== runId)
|
|
825
|
+
return;
|
|
826
|
+
if (runtime.goalContinuationTimer) {
|
|
827
|
+
clearTimeout(runtime.goalContinuationTimer);
|
|
828
|
+
runtime.goalContinuationTimer = undefined;
|
|
829
|
+
}
|
|
830
|
+
runtime.pendingContinuationRunId = undefined;
|
|
831
|
+
this.rejectOutstandingGuides(runtime, 'The provider failed before this Guide could be applied; submit again to retry.');
|
|
832
|
+
runtime.pendingNextTurn = runtime.pendingNextTurn.filter(item => {
|
|
833
|
+
const automatic = item.queueMode === 'steer'
|
|
834
|
+
|| (typeof item.message !== 'string' && item.message.hiddenUserInput === true);
|
|
835
|
+
if (automatic) {
|
|
836
|
+
runtime.runner.consumeConversationContinuation({
|
|
837
|
+
content: typeof item.message === 'string' ? item.message : item.message.text,
|
|
838
|
+
queueMode: item.queueMode,
|
|
839
|
+
clientMessageId: typeof item.message === 'string' ? undefined : item.message.clientMessageId,
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
return !automatic;
|
|
843
|
+
});
|
|
844
|
+
this.queueState(runtime);
|
|
845
|
+
this.emitQueueUpdate(runtime);
|
|
846
|
+
}
|
|
812
847
|
async run(runtime, message, options) {
|
|
813
848
|
this.applyOptions(runtime.runner, options);
|
|
814
849
|
let lastTokens = await this.runSingle(runtime, message);
|
|
@@ -914,7 +949,14 @@ class ConversationKernel {
|
|
|
914
949
|
this.consumeQueuedMessage(runtime, typeof message === 'string' ? message : message.text);
|
|
915
950
|
const timeoutMs = this.processTimeoutMs(runtime);
|
|
916
951
|
if (timeoutMs <= 0) {
|
|
917
|
-
|
|
952
|
+
let tokens;
|
|
953
|
+
try {
|
|
954
|
+
tokens = await runtime.runner.process(message);
|
|
955
|
+
}
|
|
956
|
+
catch (error) {
|
|
957
|
+
this.consumeFailedAutomaticContinuation(runtime, message, continuationMode);
|
|
958
|
+
throw error;
|
|
959
|
+
}
|
|
918
960
|
if (continuationMode)
|
|
919
961
|
runtime.runner.consumeConversationContinuation({
|
|
920
962
|
content: typeof message === 'string' ? message : message.text,
|
|
@@ -925,12 +967,19 @@ class ConversationKernel {
|
|
|
925
967
|
}
|
|
926
968
|
let timeout;
|
|
927
969
|
try {
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
970
|
+
let tokens;
|
|
971
|
+
try {
|
|
972
|
+
tokens = await Promise.race([
|
|
973
|
+
runtime.runner.process(message),
|
|
974
|
+
new Promise((_, reject) => {
|
|
975
|
+
timeout = setTimeout(() => reject(new Error(`Process timeout (${Math.round(timeoutMs / 1000)}s)`)), timeoutMs);
|
|
976
|
+
}),
|
|
977
|
+
]);
|
|
978
|
+
}
|
|
979
|
+
catch (error) {
|
|
980
|
+
this.consumeFailedAutomaticContinuation(runtime, message, continuationMode);
|
|
981
|
+
throw error;
|
|
982
|
+
}
|
|
934
983
|
if (continuationMode)
|
|
935
984
|
runtime.runner.consumeConversationContinuation({
|
|
936
985
|
content: typeof message === 'string' ? message : message.text,
|
|
@@ -944,6 +993,15 @@ class ConversationKernel {
|
|
|
944
993
|
clearTimeout(timeout);
|
|
945
994
|
}
|
|
946
995
|
}
|
|
996
|
+
consumeFailedAutomaticContinuation(runtime, message, continuationMode) {
|
|
997
|
+
if (!continuationMode || typeof message === 'string' || message.hiddenUserInput !== true)
|
|
998
|
+
return;
|
|
999
|
+
runtime.runner.consumeConversationContinuation({
|
|
1000
|
+
content: message.text,
|
|
1001
|
+
queueMode: continuationMode,
|
|
1002
|
+
clientMessageId: message.clientMessageId,
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
947
1005
|
processTimeoutMs(runtime) {
|
|
948
1006
|
const raw = runtime.runner.config.getNum('agent', 'process_timeout_ms') || this.host.config.getNum('agent', 'process_timeout_ms');
|
|
949
1007
|
if (!Number.isFinite(raw) || raw <= 0)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AgentWorkEvent } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Bounds cross-process traffic for high-rate streaming text without changing
|
|
4
|
+
* durable work-run events. Non-text events always flush pending text first.
|
|
5
|
+
*/
|
|
6
|
+
export declare class WorkEventCoalescer {
|
|
7
|
+
private readonly emit;
|
|
8
|
+
private readonly windowMs;
|
|
9
|
+
private readonly pending;
|
|
10
|
+
constructor(emit: (event: AgentWorkEvent) => void, windowMs?: number);
|
|
11
|
+
push(event: AgentWorkEvent): void;
|
|
12
|
+
flush(key: string): void;
|
|
13
|
+
flushAll(): void;
|
|
14
|
+
pendingCount(): number;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=workEventCoalescer.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WorkEventCoalescer = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Bounds cross-process traffic for high-rate streaming text without changing
|
|
6
|
+
* durable work-run events. Non-text events always flush pending text first.
|
|
7
|
+
*/
|
|
8
|
+
class WorkEventCoalescer {
|
|
9
|
+
emit;
|
|
10
|
+
windowMs;
|
|
11
|
+
pending = new Map();
|
|
12
|
+
constructor(emit, windowMs = 16) {
|
|
13
|
+
this.emit = emit;
|
|
14
|
+
this.windowMs = windowMs;
|
|
15
|
+
}
|
|
16
|
+
push(event) {
|
|
17
|
+
if (event.type !== 'text') {
|
|
18
|
+
this.flushAll();
|
|
19
|
+
this.emit(event);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const key = `${event.workspaceId || ''}::${event.conversationId}::${event.runtimeKey || ''}::${event.runId || ''}`;
|
|
23
|
+
const current = this.pending.get(key);
|
|
24
|
+
if (current) {
|
|
25
|
+
current.content += event.content;
|
|
26
|
+
current.event = event;
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const entry = {
|
|
30
|
+
event,
|
|
31
|
+
content: event.content,
|
|
32
|
+
timer: setTimeout(() => this.flush(key), this.windowMs),
|
|
33
|
+
};
|
|
34
|
+
this.pending.set(key, entry);
|
|
35
|
+
}
|
|
36
|
+
flush(key) {
|
|
37
|
+
const entry = this.pending.get(key);
|
|
38
|
+
if (!entry)
|
|
39
|
+
return;
|
|
40
|
+
this.pending.delete(key);
|
|
41
|
+
clearTimeout(entry.timer);
|
|
42
|
+
if (entry.content)
|
|
43
|
+
this.emit({ ...entry.event, content: entry.content });
|
|
44
|
+
}
|
|
45
|
+
flushAll() {
|
|
46
|
+
for (const key of [...this.pending.keys()])
|
|
47
|
+
this.flush(key);
|
|
48
|
+
}
|
|
49
|
+
pendingCount() { return this.pending.size; }
|
|
50
|
+
}
|
|
51
|
+
exports.WorkEventCoalescer = WorkEventCoalescer;
|
|
52
|
+
//# sourceMappingURL=workEventCoalescer.js.map
|
package/dist/llm/provider.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export declare class LLMProvider {
|
|
|
33
33
|
thinkingTierMaps?: Record<string, Record<string, string>> | undefined;
|
|
34
34
|
static nodeHttpTransport: ((method: 'GET' | 'POST', url: string, headers: Record<string, string>, body?: string) => Promise<NodeHttpResult>) | null;
|
|
35
35
|
static powershellTransport: ((method: 'GET' | 'POST', url: string, headers: Record<string, string>, body?: string) => Promise<NodeHttpResult>) | null;
|
|
36
|
+
private readonly temperatureUnsupported;
|
|
36
37
|
constructor(name: string, baseUrl: string, apiKey: string, explicitProtocol?: ProviderProtocol | undefined, openAIMode?: OpenAITransportMode | boolean, useProviderAdaptersV2?: boolean, requestTimeoutMs?: number,
|
|
37
38
|
/** dev-0.4.3 模型原生思考强度档位映射(模型名 → { 模型原生档位: Newmark 档位 })。 */
|
|
38
39
|
thinkingTierMaps?: Record<string, Record<string, string>> | undefined);
|
|
@@ -60,7 +61,11 @@ export declare class LLMProvider {
|
|
|
60
61
|
private isPlainHttpLoopback;
|
|
61
62
|
private transportDiagnostic;
|
|
62
63
|
private githubModelsHeaders;
|
|
64
|
+
private temperatureCapabilityKey;
|
|
65
|
+
private unsupportedTemperatureError;
|
|
66
|
+
private requestBodyForTemperatureCapability;
|
|
63
67
|
private postJsonWithFetchFallback;
|
|
68
|
+
private postJsonOnce;
|
|
64
69
|
private getJsonWithFetchFallback;
|
|
65
70
|
private shouldUseNodeHttpFallback;
|
|
66
71
|
private nodeHttpJson;
|
package/dist/llm/provider.js
CHANGED
|
@@ -101,6 +101,7 @@ class LLMProvider {
|
|
|
101
101
|
thinkingTierMaps;
|
|
102
102
|
static nodeHttpTransport = null;
|
|
103
103
|
static powershellTransport = null;
|
|
104
|
+
temperatureUnsupported = new Set();
|
|
104
105
|
constructor(name, baseUrl, apiKey, explicitProtocol, openAIMode = 'chat_stream', useProviderAdaptersV2 = false, requestTimeoutMs = DEFAULT_PROVIDER_REQUEST_TIMEOUT_MS,
|
|
105
106
|
/** dev-0.4.3 模型原生思考强度档位映射(模型名 → { 模型原生档位: Newmark 档位 })。 */
|
|
106
107
|
thinkingTierMaps) {
|
|
@@ -286,7 +287,52 @@ class LLMProvider {
|
|
|
286
287
|
'X-GitHub-Api-Version': '2022-11-28',
|
|
287
288
|
};
|
|
288
289
|
}
|
|
290
|
+
temperatureCapabilityKey(url, body) {
|
|
291
|
+
return `${url}|${String(body.model || '')}`;
|
|
292
|
+
}
|
|
293
|
+
unsupportedTemperatureError(status, raw) {
|
|
294
|
+
if (status !== 400)
|
|
295
|
+
return false;
|
|
296
|
+
try {
|
|
297
|
+
const parsed = JSON.parse(String(raw || ''));
|
|
298
|
+
if (String(parsed?.error?.param || '').toLowerCase() === 'temperature')
|
|
299
|
+
return true;
|
|
300
|
+
return /unsupported parameter[^\n]*temperature|temperature[^\n]*not supported/i.test(String(parsed?.error?.message || ''));
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return /unsupported parameter[^\n]*temperature|temperature[^\n]*not supported/i.test(String(raw || ''));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
requestBodyForTemperatureCapability(url, body) {
|
|
307
|
+
const prepared = { ...body };
|
|
308
|
+
if (this.temperatureUnsupported.has(this.temperatureCapabilityKey(url, body)))
|
|
309
|
+
delete prepared.temperature;
|
|
310
|
+
return prepared;
|
|
311
|
+
}
|
|
289
312
|
async postJsonWithFetchFallback(url, headers, body, timeoutMs = 120000, signal) {
|
|
313
|
+
const prepared = this.requestBodyForTemperatureCapability(url, body);
|
|
314
|
+
const response = await this.postJsonOnce(url, headers, prepared, timeoutMs, signal);
|
|
315
|
+
if (prepared.temperature === undefined || response.status !== 400)
|
|
316
|
+
return response;
|
|
317
|
+
const cloneable = typeof response.clone === 'function';
|
|
318
|
+
const errorText = await (cloneable ? response.clone() : response).text();
|
|
319
|
+
if (!this.unsupportedTemperatureError(response.status, errorText)) {
|
|
320
|
+
if (cloneable)
|
|
321
|
+
return response;
|
|
322
|
+
return {
|
|
323
|
+
ok: response.ok,
|
|
324
|
+
status: response.status,
|
|
325
|
+
headers: response.headers,
|
|
326
|
+
text: async () => errorText,
|
|
327
|
+
json: async () => JSON.parse(errorText || '{}'),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
this.temperatureUnsupported.add(this.temperatureCapabilityKey(url, body));
|
|
331
|
+
const retryBody = { ...body };
|
|
332
|
+
delete retryBody.temperature;
|
|
333
|
+
return this.postJsonOnce(url, headers, retryBody, timeoutMs, signal);
|
|
334
|
+
}
|
|
335
|
+
async postJsonOnce(url, headers, body, timeoutMs = 120000, signal) {
|
|
290
336
|
const effectiveTimeout = this.effectiveRequestTimeout(timeoutMs);
|
|
291
337
|
// Electron utility processes can leave an undici response body pending when
|
|
292
338
|
// several isolated workers concurrently call a plain-HTTP local provider.
|
|
@@ -1024,6 +1070,7 @@ class LLMProvider {
|
|
|
1024
1070
|
*/
|
|
1025
1071
|
buildProviderAdapterTransport() {
|
|
1026
1072
|
return async (request, signal) => {
|
|
1073
|
+
request.body = this.requestBodyForTemperatureCapability(request.url, request.body);
|
|
1027
1074
|
if (request.body?.stream === true) {
|
|
1028
1075
|
const abort = new AbortController();
|
|
1029
1076
|
const forwardAbort = () => abort.abort(signal?.reason);
|
|
@@ -1035,12 +1082,27 @@ class LLMProvider {
|
|
|
1035
1082
|
const timer = setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout);
|
|
1036
1083
|
try {
|
|
1037
1084
|
try {
|
|
1038
|
-
|
|
1085
|
+
let response = await fetch(request.url, {
|
|
1039
1086
|
method: 'POST',
|
|
1040
1087
|
headers: request.headers,
|
|
1041
1088
|
body: JSON.stringify(request.body),
|
|
1042
1089
|
signal: abort.signal,
|
|
1043
1090
|
});
|
|
1091
|
+
if (request.body.temperature !== undefined && response.status === 400) {
|
|
1092
|
+
const errorText = await response.clone().text();
|
|
1093
|
+
if (this.unsupportedTemperatureError(response.status, errorText)) {
|
|
1094
|
+
this.temperatureUnsupported.add(this.temperatureCapabilityKey(request.url, request.body));
|
|
1095
|
+
const retryBody = { ...request.body };
|
|
1096
|
+
delete retryBody.temperature;
|
|
1097
|
+
response = await fetch(request.url, {
|
|
1098
|
+
method: 'POST',
|
|
1099
|
+
headers: request.headers,
|
|
1100
|
+
body: JSON.stringify(retryBody),
|
|
1101
|
+
signal: abort.signal,
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
return response;
|
|
1044
1106
|
}
|
|
1045
1107
|
catch (error) {
|
|
1046
1108
|
if (signal?.aborted)
|
package/dist/main.js
CHANGED
|
@@ -76,6 +76,7 @@ const runtimeLifecycle_1 = require("./core/runtimeLifecycle");
|
|
|
76
76
|
const compat_1 = require("./core/compat");
|
|
77
77
|
const dshCompatibility_1 = require("./core/dshCompatibility");
|
|
78
78
|
const mcpManager_1 = require("./core/mcpManager");
|
|
79
|
+
const workEventCoalescer_1 = require("./core/workEventCoalescer");
|
|
79
80
|
const cli_help_1 = require("./cli-help");
|
|
80
81
|
const APP_NAME = 'Newmark Agent';
|
|
81
82
|
const APP_ID = 'ai.newmark.agent';
|
|
@@ -289,7 +290,7 @@ async function resetWslAgentClient() {
|
|
|
289
290
|
if (wslAgentClient)
|
|
290
291
|
await wslAgentClient.resetAgent();
|
|
291
292
|
}
|
|
292
|
-
function
|
|
293
|
+
function dispatchAgentWorkEvent(event, mirrorToMobile = true) {
|
|
293
294
|
const workEvent = event;
|
|
294
295
|
if ((workEvent.type === 'done' || workEvent.type === 'error') && workEvent.runtimeKey) {
|
|
295
296
|
browserUseEngine?.clearRuntime(workEvent.runtimeKey);
|
|
@@ -309,6 +310,20 @@ function broadcastAgentWorkEvent(event, mirrorToMobile = true) {
|
|
|
309
310
|
}
|
|
310
311
|
}
|
|
311
312
|
}
|
|
313
|
+
const workEventCoalescer = new workEventCoalescer_1.WorkEventCoalescer(event => dispatchAgentWorkEvent(event, true));
|
|
314
|
+
const workEventNoMobileCoalescer = new workEventCoalescer_1.WorkEventCoalescer(event => dispatchAgentWorkEvent(event, false));
|
|
315
|
+
function broadcastAgentWorkEvent(event, mirrorToMobile = true) {
|
|
316
|
+
const workEvent = event;
|
|
317
|
+
// Text deltas are the only high-frequency event. They are coalesced at the
|
|
318
|
+
// IPC/SSE boundary; all lifecycle/tool events retain immediate ordering.
|
|
319
|
+
if (workEvent.type === 'text') {
|
|
320
|
+
(mirrorToMobile ? workEventCoalescer : workEventNoMobileCoalescer).push(workEvent);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
workEventCoalescer.flushAll();
|
|
324
|
+
workEventNoMobileCoalescer.flushAll();
|
|
325
|
+
dispatchAgentWorkEvent(event, mirrorToMobile);
|
|
326
|
+
}
|
|
312
327
|
function ensureConversationKernel(root) {
|
|
313
328
|
if (!agent)
|
|
314
329
|
return null;
|
|
@@ -1967,7 +1982,11 @@ else {
|
|
|
1967
1982
|
? await ensureWslConversationPool().snapshot(target)
|
|
1968
1983
|
: await ensureElectronUtilityPool().snapshot(target);
|
|
1969
1984
|
const runtime = snapshot.runtime;
|
|
1970
|
-
|
|
1985
|
+
// `running` may turn false one IPC task before a finalization-
|
|
1986
|
+
// window Guide reaches the worker. Preserve the authoritative
|
|
1987
|
+
// runId and let ConversationKernel decide whether that run can
|
|
1988
|
+
// be reactivated or must reject a genuinely stale request.
|
|
1989
|
+
if (!guideText || !runtime?.runId) {
|
|
1971
1990
|
return { ok: false, error: action === 'goal_guide'
|
|
1972
1991
|
? 'There is no active Build for this Goal Guide.'
|
|
1973
1992
|
: 'Target conversation is not running.' };
|
package/dist/server.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ export interface HostedMobileServerStatus {
|
|
|
38
38
|
checkedAt: string;
|
|
39
39
|
startedAt: number;
|
|
40
40
|
}
|
|
41
|
+
export declare const MOBILE_WORKSPACE_UPLOAD_MAX_BYTES: number;
|
|
41
42
|
export declare function configureHostedServer(root: string, options?: HostedMobileServerOptions): void;
|
|
42
43
|
export declare function stopHostedServer(): Promise<void>;
|
|
43
44
|
export declare function hostedServerStatus(enabled?: boolean, probeHost?: string): Promise<HostedMobileServerStatus>;
|