newmark-agent 0.5.14 → 0.6.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 +4565 -2998
- package/dist/conversation-utility-host.js +1 -1
- package/dist/core/agent.d.ts +108 -16
- package/dist/core/agent.js +698 -195
- package/dist/core/agentKernel/agent.d.ts +1 -0
- package/dist/core/agentKernel/agent.js +9 -0
- package/dist/core/agentKernelDiagnostics.d.ts +4 -6
- package/dist/core/agentKernelDiagnostics.js +12 -9
- package/dist/core/agentKernelRunner.d.ts +5 -0
- package/dist/core/agentKernelRunner.js +250 -107
- package/dist/core/autoRouter.js +17 -27
- package/dist/core/config.d.ts +2 -0
- package/dist/core/config.js +7 -1
- package/dist/core/conversationCommandState.d.ts +17 -0
- package/dist/core/conversationCommandState.js +140 -0
- package/dist/core/conversationKernel.d.ts +25 -4
- package/dist/core/conversationKernel.js +360 -89
- package/dist/core/conversationListEvent.d.ts +6 -0
- package/dist/core/conversationListEvent.js +14 -0
- package/dist/core/electronUtilityAgentClient.d.ts +4 -1
- package/dist/core/electronUtilityAgentClient.js +4 -4
- package/dist/core/electronUtilityRuntimePool.d.ts +8 -2
- package/dist/core/electronUtilityRuntimePool.js +11 -5
- package/dist/core/installUpdate.js +41 -29
- package/dist/core/modelResponseHealth.d.ts +17 -0
- package/dist/core/modelResponseHealth.js +193 -0
- package/dist/core/providerUsageAccounting.d.ts +47 -0
- package/dist/core/providerUsageAccounting.js +71 -0
- package/dist/core/requestContextEstimate.d.ts +18 -0
- package/dist/core/requestContextEstimate.js +54 -0
- package/dist/core/subagent.d.ts +89 -5
- package/dist/core/subagent.js +264 -41
- package/dist/core/subagentCommunication.d.ts +43 -0
- package/dist/core/subagentCommunication.js +167 -0
- package/dist/core/types.d.ts +34 -1
- package/dist/core/utilityAgentProtocol.d.ts +4 -0
- package/dist/core/workEventCoalescer.js +4 -1
- package/dist/core/wslAgentClient.d.ts +18 -5
- package/dist/core/wslAgentClient.js +79 -27
- package/dist/core/wslAgentProtocol.d.ts +7 -0
- package/dist/core/wslAgentRuntimePool.d.ts +8 -2
- package/dist/core/wslAgentRuntimePool.js +19 -6
- package/dist/core/wslRuntimeProcessTree.d.ts +10 -0
- package/dist/core/wslRuntimeProcessTree.js +104 -0
- package/dist/llm/provider.d.ts +5 -12
- package/dist/llm/provider.js +150 -269
- package/dist/main.js +582 -356
- package/dist/preload.js +3 -3
- package/dist/providers/chat-completions.adapter.d.ts +2 -0
- package/dist/providers/chat-completions.adapter.js +84 -36
- package/dist/providers/provider-adapter.d.ts +2 -0
- package/dist/providers/provider-events.d.ts +23 -14
- package/dist/providers/provider-events.js +148 -43
- package/dist/providers/provider-headers.d.ts +7 -3
- package/dist/providers/provider-headers.js +109 -39
- package/dist/providers/provider-request-compat.d.ts +6 -0
- package/dist/providers/provider-request-compat.js +37 -0
- package/dist/providers/responses.adapter.d.ts +2 -0
- package/dist/providers/responses.adapter.js +192 -126
- package/dist/server.d.ts +9 -2
- package/dist/server.js +100 -24
- package/dist/tools/index.js +17 -1
- package/dist/ui/index.html +2515 -693
- package/dist/ui/lucide-sprite.svg +7 -0
- package/dist/ui/startup.html +6 -6
- package/dist/wsl-agent-host.bundle.cjs +4575 -3006
- package/dist/wsl-agent-host.js +9 -4
- package/package.json +23 -4
|
@@ -42,6 +42,7 @@ exports.toolResultObjectiveOutcome = toolResultObjectiveOutcome;
|
|
|
42
42
|
const fs = __importStar(require("fs"));
|
|
43
43
|
const path = __importStar(require("path"));
|
|
44
44
|
const terminalTakeover_1 = require("../tools/terminalTakeover");
|
|
45
|
+
const autoRouter_1 = require("./autoRouter");
|
|
45
46
|
const toolPolicy_1 = require("./toolPolicy");
|
|
46
47
|
const performanceDiagnostics_1 = require("./performanceDiagnostics");
|
|
47
48
|
const agentKernelDiagnostics_1 = require("./agentKernelDiagnostics");
|
|
@@ -280,13 +281,17 @@ function throwIfKernelAborted(signal) {
|
|
|
280
281
|
throw error;
|
|
281
282
|
}
|
|
282
283
|
async function runAgentKernel(agent) {
|
|
284
|
+
agent.acknowledgeSubagentSettlementReceipts();
|
|
283
285
|
const stopContextTimer = (0, performanceDiagnostics_1.performanceTimer)('context_prepare', { conversationId: agent.activeConversationId });
|
|
284
286
|
const processSignal = agent.activeProcessSignal();
|
|
285
287
|
if (processSignal?.aborted) {
|
|
286
288
|
stopContextTimer();
|
|
287
289
|
throwIfKernelAborted(processSignal);
|
|
288
290
|
}
|
|
289
|
-
|
|
291
|
+
// One configuration-checked provider slot per Build: retain compatibility
|
|
292
|
+
// learning and connection pools without sharing them with another Build.
|
|
293
|
+
const buildProviderCache = {};
|
|
294
|
+
if (!agent.engineModel(buildProviderCache)) {
|
|
290
295
|
const message = 'No LLM configured. Add provider in Settings > Models.';
|
|
291
296
|
agent.status = 'error';
|
|
292
297
|
agent.saveWorkspaceConversationState();
|
|
@@ -332,15 +337,25 @@ async function runAgentKernel(agent) {
|
|
|
332
337
|
const initialToolSurface = refreshToolSurface(true);
|
|
333
338
|
const assembledContext = agent.assembleContextV2(initialToolSurface.systemPromptNotice);
|
|
334
339
|
const systemPrompt = assembledContext.text;
|
|
340
|
+
const peerCacheIdentity = agent.peerRequestCacheIdentity(systemPrompt, agent.cachedToolDefinitions());
|
|
341
|
+
const peerRequestCache = agent.readPeerRequestCache(peerCacheIdentity);
|
|
342
|
+
if (peerRequestCache)
|
|
343
|
+
toolProvisioning.restore(peerRequestCache.initialTools, peerRequestCache.provisionedTools);
|
|
335
344
|
throwIfKernelAborted(processSignal);
|
|
336
|
-
|
|
337
|
-
|
|
345
|
+
// Capture request-only metadata once for this Build. Removing it after the
|
|
346
|
+
// first tool call rewrites the system prefix before all retained messages.
|
|
347
|
+
let buildTaskFocusSnapshot = peerRequestCache?.taskFocus;
|
|
348
|
+
let peerInputCheckpointed = false;
|
|
349
|
+
let lastRequestReplaySafe = true;
|
|
350
|
+
// A completed useful provider turn starts a new request recovery budget.
|
|
351
|
+
// Empty/thinking-only replies and failures never reset that budget.
|
|
352
|
+
let requestProgressGeneration = 0;
|
|
338
353
|
stopContextTimer();
|
|
339
354
|
const kernel = new NativeAgent({
|
|
340
355
|
streamFn: streamWithNewmarkProvider(agent, KernelStreamCompat),
|
|
341
356
|
toolExecution: 'parallel',
|
|
342
357
|
convertToLlm: (messages) => messages.filter(m => m.role === 'user' || m.role === 'assistant' || m.role === 'toolResult'),
|
|
343
|
-
transformContext: async (messages, signal) => transformContext(agent, messages, signal),
|
|
358
|
+
transformContext: async (messages, signal) => transformContext(agent, messages, signal, buildProviderCache),
|
|
344
359
|
resolveTools: () => {
|
|
345
360
|
const definitions = refreshToolSurface().definitions;
|
|
346
361
|
prepareAssistantToolVisibility(agent, definitions);
|
|
@@ -350,9 +365,10 @@ async function runAgentKernel(agent) {
|
|
|
350
365
|
});
|
|
351
366
|
kernel.state.systemPrompt = systemPrompt;
|
|
352
367
|
kernel.state.model = toKernelModel(agent);
|
|
353
|
-
kernel.state.tools = toKernelTools(agent,
|
|
368
|
+
kernel.state.tools = toKernelTools(agent, refreshToolSurface().definitions, toolProvisioning);
|
|
354
369
|
kernel.state.messages = toKernelMessages(agent);
|
|
355
370
|
agent.attachAgentKernelRuntime(kernel);
|
|
371
|
+
agent.flushDirectRootInbox();
|
|
356
372
|
let detachProcessAbort = () => { };
|
|
357
373
|
if (processSignal) {
|
|
358
374
|
const abortKernel = () => kernel.abort();
|
|
@@ -381,6 +397,13 @@ async function runAgentKernel(agent) {
|
|
|
381
397
|
let observedActivity = false;
|
|
382
398
|
let observedThought = false;
|
|
383
399
|
const unsubscribe = kernel.subscribe(async (event) => {
|
|
400
|
+
if (event.type === 'message_start' && event.message.role === 'assistant') {
|
|
401
|
+
// A single kernel.prompt can execute several tools before its final
|
|
402
|
+
// provider reply. Earlier tool progress cannot hide a later empty one.
|
|
403
|
+
observedActivity = false;
|
|
404
|
+
observedThought = false;
|
|
405
|
+
lastAssistant = null;
|
|
406
|
+
}
|
|
384
407
|
await handleKernelEvent(agent, event, tokens);
|
|
385
408
|
if (event.type === 'message_update') {
|
|
386
409
|
const delta = event.assistantMessageEvent;
|
|
@@ -412,15 +435,17 @@ async function runAgentKernel(agent) {
|
|
|
412
435
|
const assistant = lastAssistant;
|
|
413
436
|
const text = assistant ? KernelMessageText(assistant) : '';
|
|
414
437
|
const hasToolCall = !!assistant?.content?.some(content => content.type === 'toolCall');
|
|
438
|
+
const usableText = !!text.trim() && !agent.isLlmErrorText(text);
|
|
415
439
|
const emptyResponse = !assistant
|
|
416
440
|
|| (!text.trim() && !hasToolCall && !observedActivity && String(assistant?.stopReason || '') !== 'aborted');
|
|
417
441
|
return {
|
|
418
442
|
text: emptyResponse ? '[Error] Provider returned an empty response.' : text,
|
|
419
443
|
stopReason: String(assistant?.stopReason || ''),
|
|
420
444
|
errorMessage: String(assistant?.errorMessage || (emptyResponse ? 'Provider returned an empty response.' : '')),
|
|
421
|
-
activity: observedActivity ||
|
|
445
|
+
activity: observedActivity || usableText || hasToolCall,
|
|
422
446
|
thoughtOnly: observedThought && !text.trim() && !hasToolCall
|
|
423
447
|
&& !['error', 'aborted'].includes(String(assistant?.stopReason || '')),
|
|
448
|
+
requestReplaySafe: lastRequestReplaySafe,
|
|
424
449
|
};
|
|
425
450
|
}
|
|
426
451
|
finally {
|
|
@@ -455,15 +480,18 @@ async function runAgentKernel(agent) {
|
|
|
455
480
|
try {
|
|
456
481
|
const linkedPlanRevisionBeforeRun = agent.getLinkedPlan().revision;
|
|
457
482
|
const modelBeforeKernelRun = agent.model;
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
483
|
+
let lastTurn = await runWithCompressionResume([], false);
|
|
484
|
+
if (kernelTurnFailed(agent, lastTurn)) {
|
|
485
|
+
const visualFallback = await agent.finalVisualFallback(lastTurn.errorMessage || lastTurn.text, processSignal);
|
|
486
|
+
if (visualFallback) {
|
|
487
|
+
tokens.push({ type: 'text', text: visualFallback });
|
|
488
|
+
agent.emitWorkEvent({ type: 'final_response', content: visualFallback });
|
|
489
|
+
agent.chatMessages.push({ role: 'assistant', content: visualFallback, mode: agent.modeName(), model: agent.model, timestamp: agent.nowLabel(), runId: agent.currentWorkRunId() || undefined });
|
|
490
|
+
agent.history.push({ role: 'assistant', content: visualFallback, run_id: agent.currentWorkRunId() || undefined });
|
|
491
|
+
agent.saveWorkspaceConversationState();
|
|
492
|
+
agent.recordWorkStatus('Final visual fallback used: local mini OCR plus conservative text correction.');
|
|
493
|
+
lastTurn = { ...lastTurn, text: visualFallback, errorMessage: '', stopReason: 'stop' };
|
|
494
|
+
}
|
|
467
495
|
}
|
|
468
496
|
if (modelBeforeKernelRun && modelBeforeKernelRun !== agent.model && !tokens.some(t => t.text?.includes('[Model fallback]'))) {
|
|
469
497
|
const notice = `[Model fallback] ${modelBeforeKernelRun} unavailable; switched to ${agent.model}.`;
|
|
@@ -475,29 +503,84 @@ async function runAgentKernel(agent) {
|
|
|
475
503
|
});
|
|
476
504
|
}
|
|
477
505
|
let consecutiveEmptyResponses = 0;
|
|
506
|
+
let noProgressGeneration = requestProgressGeneration;
|
|
507
|
+
let routeRetries = 0;
|
|
508
|
+
const transientRetries = new Set();
|
|
478
509
|
for (;;) {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
510
|
+
throwIfKernelAborted(processSignal);
|
|
511
|
+
if (noProgressGeneration !== requestProgressGeneration) {
|
|
512
|
+
consecutiveEmptyResponses = 0;
|
|
513
|
+
noProgressGeneration = requestProgressGeneration;
|
|
514
|
+
}
|
|
515
|
+
// Every outcome, including one produced by a route retry, passes through
|
|
516
|
+
// the same bounded no-progress recovery. Alternating empty/thought-only
|
|
517
|
+
// responses cannot reset the five-retry allowance.
|
|
518
|
+
if (providerTurnIsEmpty(lastTurn) || lastTurn.thoughtOnly) {
|
|
519
|
+
const emptyResponseState = (0, emptyResponseRetry_1.observeEmptyResponseOutcome)(consecutiveEmptyResponses, true);
|
|
520
|
+
consecutiveEmptyResponses = emptyResponseState.consecutiveEmptyResponses;
|
|
521
|
+
if (!emptyResponseState.retry) {
|
|
522
|
+
throw new ProviderRunError(`Provider returned an empty response or only hidden reasoning for ${consecutiveEmptyResponses} consecutive requests; the recovery limit was reached.`);
|
|
523
|
+
}
|
|
524
|
+
if (lastTurn.thoughtOnly)
|
|
525
|
+
removeTrailingThoughtOnlyAssistant(kernel.state.messages);
|
|
526
|
+
else
|
|
527
|
+
removeTrailingFailedAssistant(agent, kernel.state.messages);
|
|
528
|
+
const retryNumber = consecutiveEmptyResponses;
|
|
529
|
+
const reason = lastTurn.thoughtOnly ? 'only hidden reasoning without an answer or tool call' : 'an empty response';
|
|
530
|
+
const notice = `[Model retry] Provider returned ${reason}; retrying the same deployment (${retryNumber}/${emptyResponseRetry_1.MAX_EMPTY_RESPONSE_RETRIES}) after ${(0, emptyResponseRetry_1.emptyResponseRetryDelayMs)(consecutiveEmptyResponses)}ms.`;
|
|
531
|
+
tokens.push({ type: 'text', text: notice });
|
|
532
|
+
agent.recordWorkStatus(notice);
|
|
533
|
+
await agent.waitForPlannedRouteRetry((0, emptyResponseRetry_1.emptyResponseRetryDelayMs)(consecutiveEmptyResponses));
|
|
483
534
|
lastTurn = await runWithCompressionResume([], false);
|
|
484
535
|
continue;
|
|
485
536
|
}
|
|
486
|
-
if (!
|
|
537
|
+
if (!kernelTurnFailed(agent, lastTurn) || lastTurn.requestReplaySafe === false)
|
|
487
538
|
break;
|
|
488
|
-
|
|
489
|
-
const
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
const
|
|
499
|
-
|
|
539
|
+
const failureText = lastTurn.errorMessage || lastTurn.text;
|
|
540
|
+
const failure = (0, autoRouter_1.classifyRouteFailure)(failureText);
|
|
541
|
+
const deployment = agent.activeDeployment();
|
|
542
|
+
const retryKey = `${requestProgressGeneration}:${deployment?.providerId || ''}:${deployment?.modelId || agent.activeModelName()}`;
|
|
543
|
+
// Retry-After applies to 503/408 as well as 429; the routing classifier
|
|
544
|
+
// currently retains it only for rate limits. Never shorten the server's
|
|
545
|
+
// requested wait to fit our automatic retry budget.
|
|
546
|
+
const retryAfter = failureText.match(/retry[- ]after\s*[:=]?\s*(\d+(?:\.\d+)?)\s*(ms|s|seconds?)?/i);
|
|
547
|
+
const retryAfterMs = failure.retryAfterMs ?? (retryAfter
|
|
548
|
+
? Number(retryAfter[1]) * ((retryAfter[2] || 's').toLowerCase() === 'ms' ? 1 : 1000) : undefined);
|
|
549
|
+
const retryDelayMs = retryAfterMs ?? 250;
|
|
550
|
+
const retryBudgetMs = Math.max(0, Math.min(5000, agent.lastRouteDecision?.retryBudgetMs ?? 5000));
|
|
551
|
+
const transientFailure = failure.retryable && ['transport', 'timeout', 'server_error', 'rate_limited'].includes(failure.type);
|
|
552
|
+
if (transientFailure && retryAfterMs !== undefined && retryAfterMs > retryBudgetMs)
|
|
500
553
|
break;
|
|
554
|
+
const safeTransient = transientFailure
|
|
555
|
+
&& lastTurn.requestReplaySafe === true && retryDelayMs <= retryBudgetMs;
|
|
556
|
+
const retryCurrentRequest = async () => {
|
|
557
|
+
transientRetries.add(retryKey);
|
|
558
|
+
removeTrailingFailedAssistant(agent, kernel.state.messages);
|
|
559
|
+
const notice = `[Model retry] Temporary provider failure; retrying the current request once on the same deployment after ${retryDelayMs}ms.`;
|
|
560
|
+
tokens.push({ type: 'text', text: notice });
|
|
561
|
+
agent.recordWorkStatus(notice);
|
|
562
|
+
await agent.waitForPlannedRouteRetry(retryDelayMs);
|
|
563
|
+
lastTurn = await runWithCompressionResume([], false);
|
|
564
|
+
};
|
|
565
|
+
// Fixed selections have no Auto retry planner. A completed tool from an
|
|
566
|
+
// earlier subturn does not make the *next*, still-uncommitted request
|
|
567
|
+
// unsafe to retry; its exact existing messages/results remain in place.
|
|
568
|
+
if (agent.model !== 'auto' && safeTransient && !transientRetries.has(retryKey)) {
|
|
569
|
+
await retryCurrentRequest();
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const previous = routeRetries < 2 ? agent.switchToFallbackModel(failureText) : null;
|
|
573
|
+
if (!previous) {
|
|
574
|
+
if (agent.model === 'auto' && safeTransient && !transientRetries.has(retryKey)) {
|
|
575
|
+
await retryCurrentRequest();
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
// The existing Auto same-deployment attempt consumes the same allowance;
|
|
581
|
+
// our local recovery must not add a second retry after it is exhausted.
|
|
582
|
+
if (agent.routeTransitionKind() === 'retry_same_deployment')
|
|
583
|
+
transientRetries.add(retryKey);
|
|
501
584
|
removeTrailingFailedAssistant(agent, kernel.state.messages);
|
|
502
585
|
routeRetries += 1;
|
|
503
586
|
const notice = routeTransitionNotice(agent, previous);
|
|
@@ -509,20 +592,18 @@ async function runAgentKernel(agent) {
|
|
|
509
592
|
fallback: { from: previous, to: agent.model, providerId: agent.activeDeployment()?.providerId },
|
|
510
593
|
});
|
|
511
594
|
kernel.state.model = toKernelModel(agent);
|
|
512
|
-
const
|
|
513
|
-
|
|
595
|
+
const toolSurfaceChanged = toolSurfaceIdentityForAgent(agent) !== activeToolSurfaceIdentity;
|
|
596
|
+
const fallbackToolSurface = refreshToolSurface();
|
|
597
|
+
// A same-deployment retry must retain the original Build system and
|
|
598
|
+
// provisioned schema sequence, even if a Guide changed the latest task.
|
|
599
|
+
// Actual deployment or capability transitions still refresh the surface.
|
|
600
|
+
if (toolSurfaceChanged) {
|
|
601
|
+
kernel.state.systemPrompt = [agent.buildSystemPrompt(), fallbackToolSurface.systemPromptNotice].filter(Boolean).join('\n\n');
|
|
602
|
+
}
|
|
514
603
|
kernel.state.tools = toKernelTools(agent, fallbackToolSurface.definitions, toolProvisioning);
|
|
515
604
|
await agent.waitForPlannedRouteRetry();
|
|
516
605
|
lastTurn = await runWithCompressionResume([], false);
|
|
517
606
|
}
|
|
518
|
-
if (kernelTurnFailed(agent, lastTurn)) {
|
|
519
|
-
const visualFallback = await agent.finalVisualFallback(lastTurn.errorMessage || lastTurn.text, processSignal);
|
|
520
|
-
if (visualFallback) {
|
|
521
|
-
tokens.push({ type: 'text', text: visualFallback });
|
|
522
|
-
agent.recordWorkStatus('Final visual fallback used: local mini OCR plus conservative text correction.');
|
|
523
|
-
lastTurn = { ...lastTurn, text: visualFallback, errorMessage: '', stopReason: 'stop' };
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
607
|
if (kernelTurnFailed(agent, lastTurn)) {
|
|
527
608
|
throw new ProviderRunError(normalizePublicProviderError(lastTurn.errorMessage || lastTurn.text, [agent.activeModelConfig()?.api_key]));
|
|
528
609
|
}
|
|
@@ -569,41 +650,53 @@ async function runAgentKernel(agent) {
|
|
|
569
650
|
const tools = context.tools || [];
|
|
570
651
|
const brokerOnlySurface = tools.length > 0 && tools.every(tool => tool.name === TOOL_PROVISION_NAME || tool.name === 'skill' || ALWAYS_AVAILABLE_AGENT_TOOL_NAMES.has(tool.name));
|
|
571
652
|
currentAgent.beginRouteAttempt();
|
|
653
|
+
lastRequestReplaySafe = true;
|
|
572
654
|
try {
|
|
573
|
-
const currentProvider = currentAgent.engineModel();
|
|
655
|
+
const currentProvider = currentAgent.engineModel(buildProviderCache);
|
|
574
656
|
const currentModelName = currentAgent.activeModelName();
|
|
575
657
|
if (!currentProvider || !currentModelName)
|
|
576
658
|
throw new Error('No resolved model deployment is available.');
|
|
659
|
+
if (!peerInputCheckpointed) {
|
|
660
|
+
currentAgent.checkpointPeerInput();
|
|
661
|
+
peerInputCheckpointed = true;
|
|
662
|
+
}
|
|
577
663
|
const { temperature, maxTokens, reasoningEffort } = currentProvider.intelligenceConfig(currentAgent.intelligence);
|
|
578
|
-
const newmarkMessages = fromKernelMessages(context.messages)
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
//
|
|
585
|
-
//
|
|
664
|
+
const newmarkMessages = fromKernelMessages(context.messages).map(message => message.role === 'assistant'
|
|
665
|
+
// Use the same public-content boundary on its first provider
|
|
666
|
+
// submission and after persistence. Trimming only the durable
|
|
667
|
+
// copy changes earlier tool-call envelopes on mailbox recovery.
|
|
668
|
+
? { ...message, content: currentAgent.sanitizeAssistantOutput(String(message.content || '')) }
|
|
669
|
+
: message);
|
|
670
|
+
// Tool results and Guides append to messages; neither mutable task
|
|
671
|
+
// status nor a growing tool catalog may regenerate this snapshot.
|
|
672
|
+
// Compression replaces messages explicitly, not the stable system.
|
|
673
|
+
buildTaskFocusSnapshot ??= buildRequestTaskFocus(currentAgent, context.messages, {
|
|
674
|
+
activeTools: context.tools || [],
|
|
675
|
+
toolCatalog: currentAgent.cachedToolDefinitions(),
|
|
676
|
+
});
|
|
677
|
+
if (peerCacheIdentity)
|
|
678
|
+
currentAgent.persistPeerRequestCache({
|
|
679
|
+
version: 1, identity: peerCacheIdentity, taskFocus: buildTaskFocusSnapshot,
|
|
680
|
+
...toolProvisioning.snapshot(),
|
|
681
|
+
});
|
|
586
682
|
const requestSystemPrompt = [
|
|
587
683
|
context.systemPrompt || '',
|
|
588
|
-
|
|
589
|
-
? buildRequestTaskFocus(currentAgent, context.messages, {
|
|
590
|
-
includeBootstrap,
|
|
591
|
-
compressionCompleted,
|
|
592
|
-
activeTools: context.tools || [],
|
|
593
|
-
toolCatalog: currentAgent.cachedToolDefinitions(),
|
|
594
|
-
})
|
|
595
|
-
: '',
|
|
684
|
+
buildTaskFocusSnapshot,
|
|
596
685
|
].filter(Boolean).join('\n\n');
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
686
|
+
const providerTools = toProviderToolDefinitions(context.tools || []);
|
|
687
|
+
const usageRequest = currentAgent.beginProviderUsageRequest();
|
|
688
|
+
currentAgent.recordRequestContext(usageRequest, newmarkMessages, requestSystemPrompt, providerTools, currentModelName);
|
|
689
|
+
// Sorting/serializing the complete history is diagnostic work, not
|
|
690
|
+
// required request preparation. A sink can still opt in mid-Build.
|
|
691
|
+
if ((0, agentKernelDiagnostics_1.agentKernelDiagnosticsRequested)()) {
|
|
692
|
+
(0, agentKernelDiagnostics_1.emitRequestContextDiagnostic)({
|
|
693
|
+
conversationId: currentAgent.activeConversationId,
|
|
694
|
+
systemPrompt: requestSystemPrompt,
|
|
695
|
+
messages: newmarkMessages,
|
|
696
|
+
tools: context.tools || [],
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
for await (const token of currentProvider.chatStreamWithTools(currentModelName, newmarkMessages, requestSystemPrompt, temperature, maxTokens, providerTools, options?.signal, reasoningEffort, currentAgent.config.getBool('context', 'provider_session_id')
|
|
607
700
|
? currentAgent.activeConversationId
|
|
608
701
|
: undefined)) {
|
|
609
702
|
if (!firstTokenRecorded && ((token.type === 'text' && token.text) || (token.type === 'tool_call' && token.toolCall))) {
|
|
@@ -612,15 +705,8 @@ async function runAgentKernel(agent) {
|
|
|
612
705
|
}
|
|
613
706
|
if (process.env.NEWMARK_PROVIDER_DIAGNOSTICS === '1')
|
|
614
707
|
console.error(`[NewmarkKernel] provider-token type=${token.type}`);
|
|
615
|
-
if (options?.signal?.aborted)
|
|
616
|
-
break;
|
|
617
708
|
if (token.type === 'usage' && token.usage) {
|
|
618
|
-
currentAgent.recordProviderUsage(
|
|
619
|
-
input: token.usage.input,
|
|
620
|
-
output: token.usage.output,
|
|
621
|
-
cacheRead: token.usage.cacheRead,
|
|
622
|
-
cacheWrite: token.usage.cacheWrite,
|
|
623
|
-
});
|
|
709
|
+
currentAgent.recordProviderUsage(token.usage, usageRequest);
|
|
624
710
|
(0, agentKernelDiagnostics_1.emitProviderUsageDiagnostic)({
|
|
625
711
|
conversationId: currentAgent.activeConversationId,
|
|
626
712
|
inputTokens: token.usage.input,
|
|
@@ -628,8 +714,15 @@ async function runAgentKernel(agent) {
|
|
|
628
714
|
cacheReadTokens: token.usage.cacheRead,
|
|
629
715
|
cacheWriteTokens: token.usage.cacheWrite,
|
|
630
716
|
});
|
|
717
|
+
if (options?.signal?.aborted)
|
|
718
|
+
break;
|
|
631
719
|
continue;
|
|
632
720
|
}
|
|
721
|
+
if (options?.signal?.aborted)
|
|
722
|
+
break;
|
|
723
|
+
if ((token.type === 'text' && token.text && !currentAgent.isLlmErrorText(token.text))
|
|
724
|
+
|| (token.type === 'tool_call' && token.toolCall))
|
|
725
|
+
lastRequestReplaySafe = false;
|
|
633
726
|
if (token.reasoningContent) {
|
|
634
727
|
const delta = token.reasoningContent.slice(thinking.length);
|
|
635
728
|
thinking = token.reasoningContent;
|
|
@@ -708,6 +801,8 @@ async function runAgentKernel(agent) {
|
|
|
708
801
|
}
|
|
709
802
|
const final = assistantMessage(model, finalContent, finalContent.some(c => c.type === 'toolCall') ? 'toolUse' : 'stop');
|
|
710
803
|
if (!currentAgent.isLlmErrorText(text)) {
|
|
804
|
+
if (finalContent.some(content => content.type === 'toolCall' || (content.type === 'text' && typeof content.text === 'string' && content.text.trim())))
|
|
805
|
+
requestProgressGeneration++;
|
|
711
806
|
if (brokerOnlySurface && !finalContent.some(content => content.type === 'toolCall') && text)
|
|
712
807
|
currentAgent.markRouteStreamCommitted();
|
|
713
808
|
const durationMs = Math.max(1, Date.now() - requestStartedAt);
|
|
@@ -738,7 +833,7 @@ async function runAgentKernel(agent) {
|
|
|
738
833
|
};
|
|
739
834
|
}
|
|
740
835
|
}
|
|
741
|
-
async function transformContext(agent, messages, signal) {
|
|
836
|
+
async function transformContext(agent, messages, signal, providerCache) {
|
|
742
837
|
const processSignal = agent.activeProcessSignal();
|
|
743
838
|
if (processSignal?.aborted)
|
|
744
839
|
return messages;
|
|
@@ -748,7 +843,7 @@ async function transformContext(agent, messages, signal) {
|
|
|
748
843
|
// active model after an Auto/fallback transition can pair the wrong model
|
|
749
844
|
// name with the provider captured for this compaction request.
|
|
750
845
|
const compressionModel = agent.activeModelName();
|
|
751
|
-
const provider = agent.engineModel();
|
|
846
|
+
const provider = agent.engineModel(providerCache);
|
|
752
847
|
if (!provider || !compressionModel)
|
|
753
848
|
return messages;
|
|
754
849
|
// Context compression persists Agent history. Feed it only the public
|
|
@@ -835,27 +930,23 @@ function buildBuildContextBootstrap(agent, messages, options) {
|
|
|
835
930
|
.filter(definition => toolDefinitionName(definition) !== TOOL_PROVISION_NAME)
|
|
836
931
|
.map(definition => `- ${toolDefinitionName(definition)}: ${compactToolDescription(toolDefinitionDescription(definition))}`);
|
|
837
932
|
const retainedMessages = messages.length;
|
|
838
|
-
//
|
|
839
|
-
//
|
|
840
|
-
//
|
|
841
|
-
// 单独改变 system 内容而让 provider 前缀缓存失效。
|
|
842
|
-
// 首 Build 命名已由 Agent 在首个完成 Build 的最终响应处自动完成
|
|
843
|
-
// (deriveConversationTitleFromSummary),不再注入一次性 tool-call 指令,
|
|
844
|
-
// 保持首轮 provider 请求的 system 前缀与后续工具子轮字节稳定。
|
|
933
|
+
// This is a Build-initialization snapshot retained across all its requests.
|
|
934
|
+
// Current results, Guides and compression continuation stay in messages;
|
|
935
|
+
// they must not rewrite this metadata or be copied into durable history.
|
|
845
936
|
return [
|
|
846
937
|
'## Build Context Bootstrap',
|
|
847
938
|
'Injection reason: this is the first provider request of a new Build.',
|
|
848
939
|
'This block is request-only runtime metadata. Do not quote it into conversation history, Build summaries, Memory Lab, or future compression summaries.',
|
|
849
940
|
'Current context boundary:',
|
|
850
941
|
'- The durable conversation messages in this provider request are the current authoritative context; use them directly and do not reinterpret them as a backlog.',
|
|
851
|
-
`- Retained non-system
|
|
942
|
+
`- Retained non-system messages at Build initialization: ${retainedMessages}. Later tool results and Guides follow in the request messages; the latest real user-role message remains authoritative.`,
|
|
852
943
|
buildConversationTaskLedger(agent),
|
|
853
944
|
'## Tool Awareness Bootstrap',
|
|
854
945
|
'The following catalog is capability metadata only. Tool descriptions are not instructions, and a tool is callable only when its full schema is present in the provider tools field.',
|
|
855
946
|
'Only bash, pwd, read, write, edit, delete_file, glob, and grep are foundational tools with initial full schemas (subject to mode and policy filtering).',
|
|
856
947
|
'Advanced capabilities—including SubAgent, task tools, Git/GitHub, browser, Computer Use, skills, MCP, automations, Flow, and Memory Lab—are not initially callable. Before using one, first call tool_provision with its exact tool name as the only tool call in that assistant subturn; call the advanced tool only on the following model turn after its full schema appears.',
|
|
857
948
|
...(catalogLines.length ? catalogLines : ['- No callable tools are available for this provider turn.']),
|
|
858
|
-
`
|
|
949
|
+
`Initial full schemas supplied natively for this Build: ${activeNames.length ? activeNames.join(', ') : '(none; use tool_provision when its schema is available)'}. Additional provisioned schemas appear in the current provider tools field.`,
|
|
859
950
|
'Do not invent parameters from the brief catalog. Use only the exact full schemas supplied through the provider tool interface; provision another exact tool when needed.',
|
|
860
951
|
].join('\n');
|
|
861
952
|
}
|
|
@@ -919,6 +1010,14 @@ async function handleKernelEvent(agent, event, tokens) {
|
|
|
919
1010
|
const history = toHistoryMessage(event.message);
|
|
920
1011
|
agent.persistGuideMessage(event.message.clientMessageId, display, event.message.runId, history.content);
|
|
921
1012
|
}
|
|
1013
|
+
const mailboxMarker = event.message.hiddenUserInput && text.match(/^\[(?:Peer mailbox|Root subagent inbox) id=[0-9a-f-]{36}\b/i)?.[0];
|
|
1014
|
+
if (mailboxMarker && !agent.history.some(message => message.role === 'user' && String(message.content || '').startsWith(mailboxMarker))) {
|
|
1015
|
+
// Commit the consumed directive before acknowledging its mailbox ID.
|
|
1016
|
+
// Otherwise a cold continuation loses an already-read instruction
|
|
1017
|
+
// and deletes that message from the provider's retained prefix.
|
|
1018
|
+
agent.history.push({ ...toHistoryMessage(event.message), run_id: agent.currentWorkRunId() || undefined });
|
|
1019
|
+
agent.saveWorkspaceConversationState(true);
|
|
1020
|
+
}
|
|
922
1021
|
agent.notifyAgentKernelUserMessageStart(text, event.message.clientMessageId);
|
|
923
1022
|
}
|
|
924
1023
|
else if (event.message.role === 'assistant') {
|
|
@@ -1010,9 +1109,10 @@ async function handleKernelEvent(agent, event, tokens) {
|
|
|
1010
1109
|
const publicMessage = internalProvision ? { ...event.message, content: publicContent } : event.message;
|
|
1011
1110
|
const text = agent.sanitizeAssistantOutput(KernelMessageText(publicMessage));
|
|
1012
1111
|
const failed = event.message.stopReason === 'error' || agent.isLlmErrorText(text);
|
|
1013
|
-
|
|
1112
|
+
const aborted = event.message.stopReason === 'aborted';
|
|
1113
|
+
if (!failed && !aborted)
|
|
1014
1114
|
emitBufferedAssistantText(agent, tokens);
|
|
1015
|
-
if (text && !failed)
|
|
1115
|
+
if (text && !failed && !aborted)
|
|
1016
1116
|
agent.emitWorkEvent({ type: realToolCalls.length ? 'response' : 'final_response', content: text });
|
|
1017
1117
|
if ((text || realToolCalls.length) && event.message.stopReason !== 'aborted' && !failed) {
|
|
1018
1118
|
if (text && !realToolCalls.length)
|
|
@@ -1021,6 +1121,7 @@ async function handleKernelEvent(agent, event, tokens) {
|
|
|
1021
1121
|
// Keep tool-call metadata, but never replay a hidden-reasoning line
|
|
1022
1122
|
// that was deliberately removed from the public completed message.
|
|
1023
1123
|
historyMessage.content = text;
|
|
1124
|
+
historyMessage.run_id = agent.currentWorkRunId() || undefined;
|
|
1024
1125
|
agent.history.push(historyMessage);
|
|
1025
1126
|
agent.saveWorkspaceConversationState();
|
|
1026
1127
|
}
|
|
@@ -1028,8 +1129,24 @@ async function handleKernelEvent(agent, event, tokens) {
|
|
|
1028
1129
|
resetAssistantToolVisibility(agent);
|
|
1029
1130
|
}
|
|
1030
1131
|
else if (event.message.role === 'toolResult' && event.message.toolName !== TOOL_PROVISION_NAME) {
|
|
1031
|
-
|
|
1032
|
-
agent.
|
|
1132
|
+
const receipt = event.message.details?.settlementReceipt;
|
|
1133
|
+
const historyMessage = { ...toHistoryMessage(event.message), run_id: agent.currentWorkRunId() || undefined,
|
|
1134
|
+
...(!event.message.isError && receipt ? { subagent_settlement_receipt: { ...receipt } } : {}),
|
|
1135
|
+
};
|
|
1136
|
+
agent.history.push(historyMessage);
|
|
1137
|
+
try {
|
|
1138
|
+
agent.saveWorkspaceConversationState();
|
|
1139
|
+
}
|
|
1140
|
+
catch (error) {
|
|
1141
|
+
// A later notification must not mistake this unsaved in-memory
|
|
1142
|
+
// receipt for a committed result after the persistence error.
|
|
1143
|
+
delete historyMessage.subagent_settlement_receipt;
|
|
1144
|
+
throw error;
|
|
1145
|
+
}
|
|
1146
|
+
// Retire redundant automatic wakes only after the full tool result and
|
|
1147
|
+
// its exact version receipt are durable. The receipt is never content.
|
|
1148
|
+
if (receipt && !event.message.isError)
|
|
1149
|
+
agent.acknowledgeSubagentSettlementReceipts();
|
|
1033
1150
|
}
|
|
1034
1151
|
break;
|
|
1035
1152
|
}
|
|
@@ -1045,7 +1162,7 @@ function toKernelModel(agent) {
|
|
|
1045
1162
|
provider: m?.provider || 'newmark',
|
|
1046
1163
|
baseUrl: m?.provider_url || '',
|
|
1047
1164
|
reasoning: !!m?.thinking,
|
|
1048
|
-
input:
|
|
1165
|
+
input: ['text', 'image'],
|
|
1049
1166
|
cost: {
|
|
1050
1167
|
input: Number(m?.cost_per_1k_input || 0) * 1000,
|
|
1051
1168
|
output: Number(m?.cost_per_1k_output || 0) * 1000,
|
|
@@ -1123,12 +1240,33 @@ class ToolProvisionSession {
|
|
|
1123
1240
|
this.broker = this.brokerDefinition();
|
|
1124
1241
|
}
|
|
1125
1242
|
currentDefinitions() {
|
|
1126
|
-
const active = new Set([...this.initialNames, ...this.provisionedNames]);
|
|
1127
1243
|
return [
|
|
1128
|
-
...this.catalog.filter(definition =>
|
|
1244
|
+
...this.catalog.filter(definition => this.initialNames.has(toolDefinitionName(definition))),
|
|
1129
1245
|
this.broker,
|
|
1246
|
+
// Preserve the complete previously exposed schema sequence. Loading a
|
|
1247
|
+
// new tool appends its schema instead of inserting it before the broker
|
|
1248
|
+
// or reordering earlier provisions by catalog position.
|
|
1249
|
+
...[...this.provisionedNames]
|
|
1250
|
+
.filter(name => !this.initialNames.has(name))
|
|
1251
|
+
.map(name => this.definitionsByName.get(name)),
|
|
1130
1252
|
];
|
|
1131
1253
|
}
|
|
1254
|
+
snapshot() {
|
|
1255
|
+
return { initialTools: [...this.initialNames], provisionedTools: [...this.provisionedNames] };
|
|
1256
|
+
}
|
|
1257
|
+
restore(initial, provisioned) {
|
|
1258
|
+
// Reuse ordering only through the current policy-filtered catalog. A
|
|
1259
|
+
// persisted name is never authority to restore a revoked capability.
|
|
1260
|
+
this.initialNames.clear();
|
|
1261
|
+
for (const name of initial)
|
|
1262
|
+
if (this.definitionsByName.has(name))
|
|
1263
|
+
this.initialNames.add(name);
|
|
1264
|
+
this.provisionedNames.clear();
|
|
1265
|
+
for (const name of provisioned)
|
|
1266
|
+
if (this.definitionsByName.has(name))
|
|
1267
|
+
this.provisionedNames.add(name);
|
|
1268
|
+
this.broker = this.brokerDefinition();
|
|
1269
|
+
}
|
|
1132
1270
|
metrics() {
|
|
1133
1271
|
const active = this.currentDefinitions();
|
|
1134
1272
|
return {
|
|
@@ -1356,7 +1494,9 @@ function routeToolSurfaceV2(agent, definitions, toolchain, task) {
|
|
|
1356
1494
|
const plan = planner.plan({
|
|
1357
1495
|
agentRunId: agent.runtimeActorId,
|
|
1358
1496
|
buildBlockId: agent.activeConversationId || 'build',
|
|
1359
|
-
|
|
1497
|
+
// Mailbox deltas do not change the peer's assigned capability plan.
|
|
1498
|
+
// Keep this diagnostic fingerprint out of the changing request suffix.
|
|
1499
|
+
userInput: agent.isSubagentRuntime ? (agent.subagents.get(agent.runtimeActorId)?.prompt || task) : task,
|
|
1360
1500
|
objective: '',
|
|
1361
1501
|
previousToolCalls: [],
|
|
1362
1502
|
toolUsageFrequency: new Map(),
|
|
@@ -1441,7 +1581,8 @@ function toKernelTools(agent, definitions, provisioning) {
|
|
|
1441
1581
|
};
|
|
1442
1582
|
}
|
|
1443
1583
|
const args = JSON.stringify(params || {});
|
|
1444
|
-
|
|
1584
|
+
let settlementReceipt;
|
|
1585
|
+
const rawText = await executeNewmarkTool(agent, name, args, fn.parameters, signal, receipt => { settlementReceipt = receipt; });
|
|
1445
1586
|
if (signal?.aborted) {
|
|
1446
1587
|
discardComputerUseVisionImage(name, rawText);
|
|
1447
1588
|
throw abortError();
|
|
@@ -1474,7 +1615,9 @@ function toKernelTools(agent, definitions, provisioning) {
|
|
|
1474
1615
|
}
|
|
1475
1616
|
catch { }
|
|
1476
1617
|
}
|
|
1477
|
-
return { content, details: { tool: name, ok: true, terminate, ...(launchReceipt ? { launchReceipt } : {}),
|
|
1618
|
+
return { content, details: { tool: name, ok: true, terminate, ...(launchReceipt ? { launchReceipt } : {}),
|
|
1619
|
+
...(settlementReceipt && text === rawText ? { settlementReceipt } : {}),
|
|
1620
|
+
visionImagePath: visionImage.imagePath || undefined, ephemeralVisionImage: !!visionImage.image, capturedAttachmentId: capturedInput?.id, displayImage }, terminate };
|
|
1478
1621
|
},
|
|
1479
1622
|
};
|
|
1480
1623
|
}).filter((tool) => !!tool.name);
|
|
@@ -1604,8 +1747,6 @@ function visualFallbackImageInput(agent, name, text) {
|
|
|
1604
1747
|
if (name !== 'screen_capture' && name !== 'computer_use' && name !== 'browser_use' && name !== 'pdf_read')
|
|
1605
1748
|
return {};
|
|
1606
1749
|
const model = agent.activeModelConfig();
|
|
1607
|
-
if (!model?.vision)
|
|
1608
|
-
return {};
|
|
1609
1750
|
try {
|
|
1610
1751
|
const parsed = JSON.parse(text);
|
|
1611
1752
|
const nested = name === 'pdf_read' && parsed.result && typeof parsed.result === 'object'
|
|
@@ -1627,7 +1768,7 @@ function visualFallbackImageInput(agent, name, text) {
|
|
|
1627
1768
|
return {};
|
|
1628
1769
|
}
|
|
1629
1770
|
}
|
|
1630
|
-
async function executeNewmarkTool(agent, name, args, inputSchema, signal) {
|
|
1771
|
+
async function executeNewmarkTool(agent, name, args, inputSchema, signal, onSettlementReceipt) {
|
|
1631
1772
|
const stopToolTimer = (0, performanceDiagnostics_1.performanceTimer)('tool_execution', { conversationId: agent.activeConversationId, detail: { tool: name } });
|
|
1632
1773
|
try {
|
|
1633
1774
|
const wsDir = agent.workspace.current?.path || agent.rootPath;
|
|
@@ -1663,10 +1804,13 @@ async function executeNewmarkTool(agent, name, args, inputSchema, signal) {
|
|
|
1663
1804
|
return (await agent.handleSubagentContinueEnvelope(args)).output;
|
|
1664
1805
|
if (name === 'subagent_list')
|
|
1665
1806
|
return agent.handleSubagentListEnvelope(args).output;
|
|
1666
|
-
if (name === 'subagent_read')
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1807
|
+
if (name === 'subagent_read' || name === 'subagent_result') {
|
|
1808
|
+
const result = name === 'subagent_read' ? agent.handleSubagentReadEnvelope(args) : agent.handleSubagentResultEnvelope(args);
|
|
1809
|
+
const receipt = result.metadata?.settlementReceipt;
|
|
1810
|
+
if (result.ok && receipt)
|
|
1811
|
+
onSettlementReceipt?.(receipt);
|
|
1812
|
+
return result.output;
|
|
1813
|
+
}
|
|
1670
1814
|
if (name === 'subagent_close')
|
|
1671
1815
|
return agent.handleSubagentCloseEnvelope(args).output;
|
|
1672
1816
|
if (name === 'branch_list')
|
|
@@ -1766,8 +1910,7 @@ async function executeNewmarkTool(agent, name, args, inputSchema, signal) {
|
|
|
1766
1910
|
actorId: agent.runtimeActorId,
|
|
1767
1911
|
workspaceId: (0, terminalTakeover_1.terminalTakeoverWorkspaceId)(wsDir),
|
|
1768
1912
|
backend: process.env.NEWMARK_WSL_DISTRO ? 'wsl' : (process.platform === 'win32' ? 'windows' : process.platform),
|
|
1769
|
-
allowEphemeralVisionImage: (name === 'screen_capture' || name === 'computer_use' || name === 'browser_use' || name === 'pdf_read' || name === 'ocr_read')
|
|
1770
|
-
&& !!agent.activeModelConfig()?.vision,
|
|
1913
|
+
allowEphemeralVisionImage: (name === 'screen_capture' || name === 'computer_use' || name === 'browser_use' || name === 'pdf_read' || name === 'ocr_read'),
|
|
1771
1914
|
signal,
|
|
1772
1915
|
});
|
|
1773
1916
|
if (signal?.aborted)
|