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.
@@ -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
- return await fetch(request.url, {
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?.activePromise || !runtime.runId) {
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
- const tokens = await runtime.runner.process(message);
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
- const tokens = await Promise.race([
354092
- runtime.runner.process(message),
354093
- new Promise((_3, reject) => {
354094
- timeout = setTimeout(() => reject(new Error(`Process timeout (${Math.round(timeoutMs / 1e3)}s)`)), timeoutMs);
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?.activePromise || !runtime.runId) {
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
- const tokens = await runtime.runner.process(message);
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
- const tokens = await Promise.race([
929
- runtime.runner.process(message),
930
- new Promise((_, reject) => {
931
- timeout = setTimeout(() => reject(new Error(`Process timeout (${Math.round(timeoutMs / 1000)}s)`)), timeoutMs);
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
@@ -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;
@@ -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
- return await fetch(request.url, {
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 broadcastAgentWorkEvent(event, mirrorToMobile = true) {
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
- if (!guideText || !runtime?.running || !runtime.runId) {
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>;