newmark-agent 0.4.8 → 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.
@@ -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
@@ -1,5 +1,5 @@
1
1
  import { AgentMode, AgentWorkEvent, ConversationInputEnvelope, GuideReceipt } from './types';
2
- import { ConversationQueueAction } from './conversationKernel';
2
+ import { ConversationQueueAction, ConversationQueueActionInput } from './conversationKernel';
3
3
  import { TerminalTakeoverEvent, TerminalTakeoverOwnerFilter, TerminalTakeoverState } from '../tools/terminalTakeover';
4
4
  import { WslAgentPromptRequest, WslAgentPromptResult, WslAutoRouteRatingResult, WslAgentWorkspace, WslAgentStopResult, WslConversationRewindResult, WslHostToolRequest } from './wslAgentProtocol';
5
5
  import { ConversationRuntimeTarget, NormalizedConversationTarget } from './conversationTarget';
@@ -72,13 +72,7 @@ export declare class WslAgentClient {
72
72
  snapshotTarget(target: ConversationRuntimeTarget): Promise<Record<string, unknown>>;
73
73
  rewind(target: ConversationRuntimeTarget, messageIndex: number): Promise<WslConversationRewindResult>;
74
74
  enqueueGuide(target: ConversationRuntimeTarget, envelope: ConversationInputEnvelope): Promise<GuideReceipt>;
75
- queueAction(target: ConversationRuntimeTarget, action: ConversationQueueAction, input?: {
76
- id?: string;
77
- text?: string;
78
- requestedMode?: string;
79
- goalObjective?: string;
80
- createdAt?: string;
81
- }): Promise<Record<string, unknown>>;
75
+ queueAction(target: ConversationRuntimeTarget, action: ConversationQueueAction, input?: ConversationQueueActionInput): Promise<Record<string, unknown>>;
82
76
  checkpoint(target: ConversationRuntimeTarget): Promise<Record<string, unknown>>;
83
77
  contextCompress(target: ConversationRuntimeTarget, options?: {
84
78
  keepRecent?: number;
@@ -1,5 +1,5 @@
1
1
  import { AgentMode, AgentWorkEvent, ConversationInputEnvelope, GuideReceipt } from './types';
2
- import { AgentPromptMessage, ConversationContextCompressOptions, ConversationKernelRunOptions, ConversationKernelRunResult, ConversationQueueAction, ConversationQueueMode, ConversationStopResult } from './conversationKernel';
2
+ import { AgentPromptMessage, ConversationContextCompressOptions, ConversationKernelRunOptions, ConversationKernelRunResult, ConversationQueueAction, ConversationQueueActionInput, ConversationQueueMode, ConversationStopResult } from './conversationKernel';
3
3
  import { ConversationRuntimeTarget } from './conversationTarget';
4
4
  import { TerminalTakeoverEvent, TerminalTakeoverOwnerFilter, TerminalTakeoverState } from '../tools/terminalTakeover';
5
5
  import { BrowserUseRequest } from './browserUse';
@@ -117,13 +117,7 @@ export type WslAgentRequest = {
117
117
  params: {
118
118
  target: ConversationRuntimeTarget;
119
119
  action: ConversationQueueAction;
120
- input?: {
121
- id?: string;
122
- text?: string;
123
- requestedMode?: string;
124
- goalObjective?: string;
125
- createdAt?: string;
126
- };
120
+ input?: ConversationQueueActionInput;
127
121
  };
128
122
  } | {
129
123
  id: string;
@@ -1,5 +1,5 @@
1
1
  import { AgentMode, AgentWorkEvent, ConversationInputEnvelope, GuideReceipt } from './types';
2
- import { ConversationQueueAction } from './conversationKernel';
2
+ import { ConversationQueueAction, ConversationQueueActionInput } from './conversationKernel';
3
3
  import { ConversationRuntimeTarget, NormalizedConversationTarget } from './conversationTarget';
4
4
  import { WslHostToolHandler } from './wslAgentClient';
5
5
  import { WslAgentPromptRequest, WslAgentPromptResult, WslAutoRouteRatingResult, WslAgentStopResult, WslConversationRewindResult } from './wslAgentProtocol';
@@ -11,13 +11,7 @@ export interface WslTargetRuntimeClient {
11
11
  rewind(target: ConversationRuntimeTarget, messageIndex: number): Promise<WslConversationRewindResult>;
12
12
  requestStop(target: ConversationRuntimeTarget, runId?: string): Promise<WslAgentStopResult>;
13
13
  enqueueGuide(target: ConversationRuntimeTarget, envelope: ConversationInputEnvelope): Promise<GuideReceipt>;
14
- queueAction?(target: ConversationRuntimeTarget, action: ConversationQueueAction, input?: {
15
- id?: string;
16
- text?: string;
17
- requestedMode?: string;
18
- goalObjective?: string;
19
- createdAt?: string;
20
- }): Promise<Record<string, unknown>>;
14
+ queueAction?(target: ConversationRuntimeTarget, action: ConversationQueueAction, input?: ConversationQueueActionInput): Promise<Record<string, unknown>>;
21
15
  checkpoint(target: ConversationRuntimeTarget): Promise<Record<string, unknown>>;
22
16
  contextCompress?(target: ConversationRuntimeTarget, options?: {
23
17
  keepRecent?: number;
@@ -81,13 +75,7 @@ export declare class WslAgentRuntimePool {
81
75
  rewind(target: ConversationRuntimeTarget, messageIndex: number): Promise<WslConversationRewindResult>;
82
76
  requestStop(target: ConversationRuntimeTarget, runId?: string): Promise<WslPoolStopResult>;
83
77
  enqueueGuide(envelope: ConversationInputEnvelope): Promise<GuideReceipt>;
84
- queueAction(target: ConversationRuntimeTarget, action: ConversationQueueAction, input?: {
85
- id?: string;
86
- text?: string;
87
- requestedMode?: string;
88
- goalObjective?: string;
89
- createdAt?: string;
90
- }): Promise<Record<string, unknown>>;
78
+ queueAction(target: ConversationRuntimeTarget, action: ConversationQueueAction, input?: ConversationQueueActionInput): Promise<Record<string, unknown>>;
91
79
  checkpoint(target: ConversationRuntimeTarget): Promise<Record<string, unknown>>;
92
80
  contextCompress(target: ConversationRuntimeTarget, options?: {
93
81
  keepRecent?: number;
@@ -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)