newmark-agent 0.5.4 → 0.5.7

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.
@@ -220,6 +220,10 @@
220
220
  <symbol id="minus" viewBox="0 0 24 24">
221
221
  <path d="M5 12h14" />
222
222
  </symbol>
223
+ <symbol id="mouse" viewBox="0 0 24 24">
224
+ <rect x="5" y="2" width="14" height="20" rx="7" />
225
+ <path d="M12 6v4" />
226
+ </symbol>
223
227
  <symbol id="move" viewBox="0 0 24 24">
224
228
  <path d="M12 2v20" />
225
229
  <path d="m15 19-3 3-3-3" />
@@ -328674,7 +328674,7 @@ function providerStreamTimeoutError(timeoutMs) {
328674
328674
  error.message = `Stream read timeout after ${timeoutMs}ms`;
328675
328675
  return error;
328676
328676
  }
328677
- async function readProviderStreamChunk(reader, signal, timeoutMs = 3e4) {
328677
+ async function readProviderStreamChunk(reader, signal, timeoutMs = 0) {
328678
328678
  if (signal.aborted) throw providerAbortError(signal);
328679
328679
  let timer;
328680
328680
  let onAbort;
@@ -328682,9 +328682,9 @@ async function readProviderStreamChunk(reader, signal, timeoutMs = 3e4) {
328682
328682
  onAbort = () => reject(providerAbortError(signal));
328683
328683
  signal.addEventListener("abort", onAbort, { once: true });
328684
328684
  });
328685
- const timeoutPromise = new Promise((_3, reject) => {
328685
+ const timeoutPromise = timeoutMs > 0 ? new Promise((_3, reject) => {
328686
328686
  timer = setTimeout(() => reject(providerStreamTimeoutError(timeoutMs)), timeoutMs);
328687
- });
328687
+ }) : new Promise(() => void 0);
328688
328688
  try {
328689
328689
  return await Promise.race([reader.read(), abortPromise, timeoutPromise]);
328690
328690
  } catch (error) {
@@ -340861,7 +340861,13 @@ async function runAgentKernel(agent) {
340861
340861
  agent.recordWorkStatus("Final visual fallback used: local mini OCR plus conservative text correction.");
340862
340862
  }
340863
340863
  if (modelBeforeKernelRun && modelBeforeKernelRun !== agent.model && !tokens.some((t3) => t3.text?.includes("[Model fallback]"))) {
340864
- tokens.unshift({ type: "text", text: `[Model fallback] ${modelBeforeKernelRun} unavailable; switched to ${agent.model}.` });
340864
+ const notice = `[Model fallback] ${modelBeforeKernelRun} unavailable; switched to ${agent.model}.`;
340865
+ tokens.unshift({ type: "text", text: notice });
340866
+ agent.emitWorkEvent({
340867
+ type: "status",
340868
+ content: notice,
340869
+ fallback: { from: modelBeforeKernelRun, to: agent.model, providerId: agent.activeDeployment()?.providerId }
340870
+ });
340865
340871
  }
340866
340872
  let emptyResponseRetries = 0;
340867
340873
  while (providerTurnIsEmpty(lastTurn) && emptyResponseRetries < 2) {
@@ -340882,6 +340888,11 @@ async function runAgentKernel(agent) {
340882
340888
  const notice = routeTransitionNotice(agent, previous);
340883
340889
  tokens.push({ type: "text", text: notice });
340884
340890
  agent.recordWorkStatus(notice);
340891
+ agent.emitWorkEvent({
340892
+ type: "status",
340893
+ content: notice,
340894
+ fallback: { from: previous, to: agent.model, providerId: agent.activeDeployment()?.providerId }
340895
+ });
340885
340896
  kernel2.state.model = toKernelModel(agent);
340886
340897
  const fallbackToolSurface = refreshToolSurface(true);
340887
340898
  kernel2.state.systemPrompt = [agent.buildSystemPrompt(), fallbackToolSurface.systemPromptNotice].filter(Boolean).join("\n\n");
@@ -343306,14 +343317,16 @@ var AutoRouter = class {
343306
343317
  retryDelayMs
343307
343318
  });
343308
343319
  }
343320
+ if (failure.allowModelFallback === false) return attempts.slice(0, remainingAttempts);
343309
343321
  const selection = decision.requestedSelection;
343310
343322
  if (selection.kind === "fixed") return attempts.slice(0, remainingAttempts);
343311
343323
  const scope = selection.kind === "auto" ? selection.scope : { kind: "provider", providerId: current.providerId };
343312
343324
  const subset = selection.kind === "auto" ? selection.subset : void 0;
343313
343325
  const currentGroup = current.logicalModelGroupId;
343326
+ const currentProviderId = current.providerId;
343314
343327
  const now2 = this.now();
343315
343328
  const attemptedDeployments = decision.attempts.map((attempt) => attempt.deployment);
343316
- const eligible = candidates.filter((candidate) => candidate.enabled && inScope(candidate.deployment, scope) && inSubset(candidate.deployment, subset) && !sameDeployment(candidate.deployment, current) && !attemptedDeployments.some((attempted) => sameDeployment(candidate.deployment, attempted)) && validationEligible(candidate, now2).length === 0 && this.passedInitialHardFilters(decision, candidate) && this.circuitState(candidate.deployment, now2, false) !== "open");
343329
+ const eligible = candidates.filter((candidate) => candidate.enabled && candidate.deployment.providerId === currentProviderId && inScope(candidate.deployment, scope) && inSubset(candidate.deployment, subset) && !sameDeployment(candidate.deployment, current) && !attemptedDeployments.some((attempted) => sameDeployment(candidate.deployment, attempted)) && validationEligible(candidate, now2).length === 0 && this.passedInitialHardFilters(decision, candidate) && this.circuitState(candidate.deployment, now2, false) !== "open");
343317
343330
  const equivalent = currentGroup ? eligible.find((candidate) => candidate.deployment.logicalModelGroupId === currentGroup && !candidate.fallbackOnly) : void 0;
343318
343331
  const fallback = eligible.find((candidate) => candidate.fallbackOnly);
343319
343332
  const rankedAlternates = decision.rankedCandidates.map((ranked) => eligible.find((candidate) => sameDeployment(candidate.deployment, ranked.deployment))).filter((candidate) => !!candidate && candidate !== equivalent && candidate !== fallback && !candidate.fallbackOnly);
@@ -346751,7 +346764,8 @@ ${String(event.toolArgs || "")}`;
346751
346764
  sequence,
346752
346765
  status: input2.status,
346753
346766
  guide: !isToolEvent && input2.guide ? this.normalizeGuideReceipt(input2.guide) : void 0,
346754
- displayImage: isToolEvent ? this.hydrateDisplayImage(input2.displayImage) : void 0
346767
+ displayImage: isToolEvent ? this.hydrateDisplayImage(input2.displayImage) : void 0,
346768
+ fallback: input2.fallback
346755
346769
  };
346756
346770
  if (activeRun && this.isPersistablePublicWorkEvent(event)) {
346757
346771
  activeRun.sequence = Number(sequence || activeRun.sequence + 1);
@@ -349670,9 +349684,15 @@ ${summary}`, segment, "local-summarize", true);
349670
349684
  }
349671
349685
  compressionBuildBlockStart(messages) {
349672
349686
  const activeRunId = this.currentWorkRunId();
349673
- if (!activeRunId) return 0;
349674
- const index = messages.findIndex((message) => String(message.run_id || message.runId || "") === activeRunId);
349675
- return index >= 0 ? index : 0;
349687
+ if (activeRunId) {
349688
+ const index = messages.findIndex((message) => String(message.run_id || message.runId || "") === activeRunId);
349689
+ if (index >= 0) return index;
349690
+ }
349691
+ let lastRunBoundary = -1;
349692
+ for (let index = 0; index < messages.length; index += 1) {
349693
+ if (String(messages[index]?.run_id || messages[index]?.runId || "")) lastRunBoundary = index + 1;
349694
+ }
349695
+ return lastRunBoundary >= 0 ? lastRunBoundary : messages.length;
349676
349696
  }
349677
349697
  recentContextSuffix(messages, maxMessages, tokenBudget) {
349678
349698
  if (!messages.length) return [];
@@ -350601,17 +350621,12 @@ ${msg.content}
350601
350621
  currentAttempt.sideEffectBoundary = this.routeSideEffectCommitted;
350602
350622
  if (this.routeAttemptStartedAt > 0) currentAttempt.durationMs = Math.max(0, Date.now() - this.routeAttemptStartedAt);
350603
350623
  }
350604
- if (!fallbackEnabled) {
350605
- this.lastRouteDecision.finalStatus = "failed";
350606
- this.routeAttemptStartedAt = 0;
350607
- this.persistRouteDecision(this.lastRouteDecision);
350608
- return null;
350609
- }
350610
350624
  if (!this.pendingAutoAttempts.length) {
350611
350625
  this.pendingAutoAttempts = this.autoRouter.planAttempts(this.lastRouteDecision, this.autoRouteCandidates(), {
350612
350626
  error: failure,
350613
350627
  streamCommitted: this.routeStreamCommitted,
350614
- sideEffectCommitted: this.routeSideEffectCommitted
350628
+ sideEffectCommitted: this.routeSideEffectCommitted,
350629
+ allowModelFallback: fallbackEnabled
350615
350630
  });
350616
350631
  }
350617
350632
  const next2 = this.pendingAutoAttempts.shift();
@@ -350646,7 +350661,6 @@ ${msg.content}
350646
350661
  if (!next?.name) return null;
350647
350662
  this.model = next.name;
350648
350663
  this.fixedDeployment = this.deploymentRef(next);
350649
- if (next.provider) this.config.set("models", "auto_switch_anchor_provider", next.provider);
350650
350664
  return current;
350651
350665
  }
350652
350666
  isLlmErrorText(text) {
@@ -350654,12 +350668,10 @@ ${msg.content}
350654
350668
  }
350655
350669
  scopedSwitchModels(currentModelName) {
350656
350670
  const all = this.config.allModels();
350657
- if (this.config.autoSwitchScope() !== "provider") return all;
350658
- const current = currentModelName === "auto" ? void 0 : this.config.findModel(currentModelName);
350659
- const providerId = current?.provider_id || this.config.autoSwitchAnchorProvider() || this.config.findModel(this.config.getStr("models", "default_model"))?.provider_id || all[0]?.provider_id || "";
350660
- if (!providerId) return all;
350661
- const provider = this.config.findProvider(providerId);
350662
- return all.filter((m2) => m2.provider_id === (provider?.id || providerId));
350671
+ const current = currentModelName === "auto" ? this.activeModelConfig() : this.config.findModel(currentModelName);
350672
+ const providerId = current?.provider_id || this.fixedDeployment?.providerId || (currentModelName !== "auto" ? this.activeDeployment()?.providerId : void 0);
350673
+ if (!providerId) return [];
350674
+ return all.filter((m2) => m2.provider_id === providerId);
350663
350675
  }
350664
350676
  async validateModels(selectedNames, options = {}) {
350665
350677
  if (this.modelValidationPromise) return this.modelValidationPromise;
@@ -352041,7 +352053,10 @@ ${items.map((item) => this.formatAutomation(item)).join("\n")}`;
352041
352053
  if (!sa) return "[Subagent] Not found.";
352042
352054
  const requestedModel = this.normalizeSubagentModelSelection(sa.model && sa.model !== "default" ? sa.model : this.modelSelectionValue() || this.model);
352043
352055
  const requestedDeployment = parseDeploymentSelectionValue2(requestedModel);
352044
- const assignedModel = requestedModel === "auto" ? this.activeModelConfig() : requestedDeployment ? this.config.findDeployment(requestedDeployment) : this.config.findModel(requestedModel);
352056
+ let assignedModel = requestedModel === "auto" ? this.activeModelConfig() : requestedDeployment ? this.config.findDeployment(requestedDeployment) : this.config.findModel(requestedModel);
352057
+ if (!assignedModel && !requestedDeployment && requestedModel !== "auto") {
352058
+ assignedModel = this.activeModelConfig();
352059
+ }
352045
352060
  const model = assignedModel?.name || (requestedModel === "auto" ? this.activeModelName() : requestedModel);
352046
352061
  const activeModel = this.activeModelConfig();
352047
352062
  const activeProvider = this.engineModel();
@@ -353470,6 +353485,32 @@ var ConversationKernel = class {
353470
353485
  followUp: queued?.followUp.slice() || []
353471
353486
  };
353472
353487
  }
353488
+ /**
353489
+ * dev-0.5.6: 排队 followUp 消息 drain 时转成普通用户消息载荷。
353490
+ *
353491
+ * 入队时(enqueueSameSession)为去重/编辑保留了 clientMessageId,并把
353492
+ * runId 固定为入队时运行的 runId;若原样传给 process,写入 chatMessages
353493
+ * 的用户消息会携带旧 runId + clientMessageId,渲染端(PC renderTranscript /
353494
+ * 移动端 projectRemoteConversationItems)会把「clientMessageId + runId 命中
353495
+ * workRuns」的消息误判为 guide 消息而跳过气泡,导致排队自动发送的消息不
353496
+ * 显示在用户输入时间线。清除这两个字段后 process 走普通用户消息分支
353497
+ * (chatMessages.push,mode 取 visibleMode,runId 取当前 run)。
353498
+ */
353499
+ drainQueuedFollowUpMessage(message) {
353500
+ if (typeof message === "string") return message;
353501
+ const text = String(message.text || "");
353502
+ const images = message.images;
353503
+ const attachments = message.attachments;
353504
+ const visible = message.visibleUserInput;
353505
+ const visibleMode = message.visibleMode;
353506
+ return {
353507
+ text,
353508
+ ...images?.length ? { images } : {},
353509
+ ...attachments?.length ? { attachments } : {},
353510
+ ...visible ? { visibleUserInput: visible } : {},
353511
+ ...visibleMode ? { visibleMode } : {}
353512
+ };
353513
+ }
353473
353514
  queueItems(target) {
353474
353515
  const runtime = this.findRuntime(target);
353475
353516
  if (!runtime) return [];
@@ -354211,7 +354252,7 @@ ${batchText}`,
354211
354252
  };
354212
354253
  lastTokens = await this.runSingle(runtime, batchMessage, "steer");
354213
354254
  } else {
354214
- lastTokens = await this.runSingle(runtime, next.message, next.queueMode);
354255
+ lastTokens = await this.runSingle(runtime, this.drainQueuedFollowUpMessage(next.message), next.queueMode);
354215
354256
  }
354216
354257
  }
354217
354258
  const rootMessage = runtime.runner.subagents.readRootInbox()[0];
@@ -354670,7 +354711,11 @@ ${text}`;
354670
354711
  type: "queue_update",
354671
354712
  content: "Conversation queue updated.",
354672
354713
  conversationId: runtime.id,
354673
- queue: this.queued(runtime.target)
354714
+ queue: this.queued(runtime.target),
354715
+ // Structured rows with stable kernel ids so every consumer (PC UI and
354716
+ // the paired mobile client) can render/edit/delete the same items.
354717
+ queueItems: this.queueItems(runtime.target),
354718
+ queuePaused: runtime.queuePaused === true
354674
354719
  });
354675
354720
  }
354676
354721
  clearQueued(runtime) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "newmark-agent",
3
3
  "productName": "Newmark Agent",
4
- "version": "0.5.4",
4
+ "version": "0.5.7",
5
5
  "description": "Newmark Agent — Portable AI coding agent with rich GUI and CLI (TypeScript)",
6
6
  "homepage": "https://github.com/positer/Newmark-Agent",
7
7
  "repository": {
@@ -53,7 +53,7 @@
53
53
  "test:desktop": "npm run build && node dist/tests/conversationHistoryFirstVerify.js && npm run test:desktop:built",
54
54
  "test:deletion-safety": "npm run build && npm run test:deletion-safety:built",
55
55
  "test:deletion-safety:built": "node scripts/deletion-safety-stress.cjs",
56
- "test:desktop:built": "node dist/tests/verify.js && node dist/tests/thinkingTierMapCacheStressVerify.js && node dist/tests/computerUseSessionVerify.js && node dist/tests/contextSystemV2Verify.js && node dist/tests/contextSystemV2StressVerify.js && node dist/tests/providerAdapterV2Verify.js && node dist/tests/providerTimeoutRecoveryVerify.js && node dist/tests/agentRuntimeV2Verify.js && node dist/tests/toolchainExposureV2Verify.js && node dist/tests/localOcrFallbackVerify.js && node dist/tests/dev018ModeSubagentStressVerify.js && node dist/tests/conversationBranchStressVerify.js && node dist/tests/conversationArchiveConcurrencyVerify.js && node dist/tests/conversationArchiveRuntimeVerify.js && node dist/tests/dev040ComprehensiveStressVerify.js && node dist/tests/memoryPolicyVerify.js && node dist/tests/newmarkSelectVerify.js && node dist/tests/mcpManagerVerify.js && node dist/tests/dshCompatibilityVerify.js && node dist/tests/performanceOptimizationVerify.js && node scripts/compression-pressure-stress.cjs && node dist/tests/compressionFidelityVerify.js && node dist/tests/responseTrajectoryVerify.js && node dist/tests/normalChatRegressionVerify.js && node dist/tests/dev008-subagent.js && node dist/tests/dev009Verify.js && node dist/tests/runtimeIsolationVerify.js && node dist/tests/runtimePoolCapacityVerify.js && node dist/tests/guideWorkRunVerify.js && node dist/tests/modelSwitchBehaviorVerify.js && node scripts/flow-pause-stop-draft-stress.cjs && node dist/tests/guideUiReconcileVerify.js && node dist/tests/queueAttachmentIsolationVerify.js && node dist/tests/workspaceMenuVerify.js && node dist/tests/userImagePersistenceVerify.js && node dist/tests/displayImageVerify.js && node dist/tests/visualPreferencesVerify.js && node dist/tests/browserUseVerify.js && node dist/tests/toolProcessVerify.js && node dist/tests/toolProvisioningVerify.js && node dist/tests/taskToolsVerify.js && node dist/tests/contextCacheHitStressVerify.js && node dist/tests/startupPrewarmVerify.js && node dist/tests/pdfPreviewServerVerify.js && node dist/tests/editorLifecycleVerify.js && node dist/tests/terminalTakeoverVerify.js && node dist/tests/workspaceFileRouterVerify.js && node dist/tests/screenCaptureIndependentVerify.js && node dist/tests/computerUsePerformanceVerify.js && node dist/tests/autoRouterVerify.js && node dist/tests/autoAgentIntegrationVerify.js && node dist/tests/modelRecoveryStressVerify.js && node dist/tests/autoRouteRatingVerify.js && node dist/tests/providerIdentityVerify.js && node dist/tests/modelValidationVerify.js && node dist/tests/modelValidationAgentIntegrationVerify.js && node scripts/deletion-safety-stress.cjs",
56
+ "test:desktop:built": "node dist/tests/verify.js && node dist/tests/thinkingTierMapCacheStressVerify.js && node dist/tests/computerUseSessionVerify.js && node dist/tests/contextSystemV2Verify.js && node dist/tests/contextSystemV2StressVerify.js && node dist/tests/providerAdapterV2Verify.js && node dist/tests/providerTimeoutRecoveryVerify.js && node dist/tests/agentRuntimeV2Verify.js && node dist/tests/toolchainExposureV2Verify.js && node dist/tests/localOcrFallbackVerify.js && node dist/tests/dev018ModeSubagentStressVerify.js && node dist/tests/conversationBranchStressVerify.js && node dist/tests/conversationArchiveConcurrencyVerify.js && node dist/tests/conversationArchiveRuntimeVerify.js && node dist/tests/dev040ComprehensiveStressVerify.js && node dist/tests/memoryPolicyVerify.js && node dist/tests/newmarkSelectVerify.js && node dist/tests/mcpManagerVerify.js && node dist/tests/dshCompatibilityVerify.js && node dist/tests/performanceOptimizationVerify.js && node scripts/compression-pressure-stress.cjs && node dist/tests/compressionFidelityVerify.js && node dist/tests/responseTrajectoryVerify.js && node dist/tests/normalChatRegressionVerify.js && node dist/tests/dev008-subagent.js && node dist/tests/dev009Verify.js && node dist/tests/runtimeIsolationVerify.js && node dist/tests/runtimePoolCapacityVerify.js && node dist/tests/guideWorkRunVerify.js && node dist/tests/modelSwitchBehaviorVerify.js && node scripts/flow-pause-stop-draft-stress.cjs && node dist/tests/guideUiReconcileVerify.js && node dist/tests/workReviewDedupVerify.js && node dist/tests/queueAttachmentIsolationVerify.js && node dist/tests/queueDrainUserMessageVerify.js && node dist/tests/pcGlassMigrationVerify.js && node dist/tests/longHistoryDisplayVerify.js && node dist/tests/workspaceMenuVerify.js && node dist/tests/userImagePersistenceVerify.js && node dist/tests/displayImageVerify.js && node dist/tests/visualPreferencesVerify.js && node dist/tests/browserUseVerify.js && node dist/tests/toolProcessVerify.js && node dist/tests/toolProvisioningVerify.js && node dist/tests/taskToolsVerify.js && node dist/tests/contextCacheHitStressVerify.js && node dist/tests/startupPrewarmVerify.js && node dist/tests/pdfPreviewServerVerify.js && node dist/tests/editorLifecycleVerify.js && node dist/tests/terminalTakeoverVerify.js && node dist/tests/workspaceFileRouterVerify.js && node dist/tests/screenCaptureIndependentVerify.js && node dist/tests/computerUsePerformanceVerify.js && node dist/tests/autoRouterVerify.js && node dist/tests/autoAgentIntegrationVerify.js && node dist/tests/modelRecoveryStressVerify.js && node dist/tests/autoRouteRatingVerify.js && node dist/tests/providerIdentityVerify.js && node dist/tests/modelValidationVerify.js && node dist/tests/modelValidationAgentIntegrationVerify.js && node scripts/deletion-safety-stress.cjs",
57
57
  "test:conversation-branch-stress": "npm run build && node dist/tests/conversationBranchStressVerify.js",
58
58
  "test:conversation-archive-concurrency": "npm run build && node dist/tests/conversationArchiveConcurrencyVerify.js",
59
59
  "test:memory-policy": "npm run build && node dist/tests/memoryPolicyVerify.js",
@@ -126,6 +126,9 @@
126
126
  "release:ui-gemma-removal-smoke": "node scripts/release-ui-gemma-removal-smoke.cjs",
127
127
  "release:ui-icon-smoke": "node scripts/release-ui-icon-smoke.cjs",
128
128
  "release:ui-render-performance-smoke": "node scripts/release-ui-render-performance-smoke.cjs",
129
+ "test:liquid-renderer-calls": "node scripts/measure-liquid-renderer-calls.cjs",
130
+ "test:liquid-renderer-electron": "node scripts/dev-liquid-renderer-performance-smoke.cjs",
131
+ "release:ui-incremental-render-stress": "node scripts/release-ui-incremental-render-stress.cjs",
129
132
  "release:ui-flow-subagent-smoke": "node scripts/release-ui-flow-subagent-smoke.cjs",
130
133
  "release:ui-media-md-smoke": "node scripts/release-ui-media-md-smoke.cjs",
131
134
  "release:ui-native-editor-smoke": "node scripts/release-ui-native-editor-smoke.cjs",
@@ -184,7 +187,9 @@
184
187
  "dist:mac": "npm run build:clean && electron-builder --mac",
185
188
  "dist:windows-release": "npm run test:full-release && npm run build:clean && node scripts/dist-portable.cjs",
186
189
  "release:ocr-budget-smoke": "node scripts/verify-ocr-package-budget.cjs",
187
- "release:packaged-ocr-smoke": "node scripts/release-packaged-ocr-smoke.cjs"
190
+ "release:packaged-ocr-smoke": "node scripts/release-packaged-ocr-smoke.cjs",
191
+ "test:stream-unlimited-timeout-stress": "npm run build && node dist/tests/streamUnlimitedTimeoutStressVerify.js",
192
+ "test:queue-unify-stress": "npm run build && node dist/tests/queueUnifyStressVerify.js && node dist/tests/queueDrainUserMessageVerify.js"
188
193
  },
189
194
  "build": {
190
195
  "npmRebuild": false,