dominds 1.23.3 → 1.23.5

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/dialog.d.ts CHANGED
@@ -354,6 +354,25 @@ export declare abstract class Dialog {
354
354
  skipTaskdoc?: boolean;
355
355
  sideDialogReplyTarget: DialogSideDialogReplyTarget;
356
356
  }): DialogQueuedPromptState;
357
+ private persistPendingRuntimePrompt;
358
+ private runtimePromptCommon;
359
+ queueRuntimeReplyPrompt(options: {
360
+ prompt: string;
361
+ msgId: string;
362
+ grammar: 'markdown';
363
+ userLanguageCode?: LanguageCode;
364
+ tellaskReplyDirective: TellaskReplyDirective;
365
+ skipTaskdoc?: boolean;
366
+ }): Promise<DialogQueuedPromptState>;
367
+ queueRuntimeSideDialogPrompt(options: {
368
+ prompt: string;
369
+ msgId: string;
370
+ grammar: 'markdown';
371
+ userLanguageCode?: LanguageCode;
372
+ tellaskReplyDirective: TellaskReplyDirective;
373
+ skipTaskdoc?: boolean;
374
+ sideDialogReplyTarget: DialogSideDialogReplyTarget;
375
+ }): Promise<DialogQueuedPromptState>;
357
376
  hasUpNext(): boolean;
358
377
  peekUpNext(): DialogQueuedPromptState | undefined;
359
378
  takeUpNext(): DialogQueuedPromptState | undefined;
@@ -678,6 +697,7 @@ export declare abstract class DialogStore {
678
697
  * Start a new course in storage
679
698
  */
680
699
  startNewCourse(_dialog: Dialog, _newCoursePrompt: DialogRuntimePrompt): Promise<void>;
700
+ persistPendingRuntimePrompt(_dialog: Dialog, _prompt: DialogRuntimePrompt): Promise<void>;
681
701
  /**
682
702
  * Handle stream error
683
703
  */
package/dist/dialog.js CHANGED
@@ -1018,6 +1018,65 @@ class Dialog {
1018
1018
  this._updatedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
1019
1019
  return merged;
1020
1020
  }
1021
+ async persistPendingRuntimePrompt(prompt) {
1022
+ await this.dlgStore.persistPendingRuntimePrompt(this, prompt);
1023
+ }
1024
+ runtimePromptCommon(options) {
1025
+ const trimmed = options.prompt.trim();
1026
+ if (!trimmed) {
1027
+ throw new Error('Prompt is required to queue runtime prompt');
1028
+ }
1029
+ return {
1030
+ prompt: trimmed,
1031
+ msgId: options.msgId,
1032
+ grammar: options.grammar,
1033
+ userLanguageCode: options.userLanguageCode ?? this._lastUserLanguageCode,
1034
+ origin: 'runtime',
1035
+ runControl: undefined,
1036
+ };
1037
+ }
1038
+ async queueRuntimeReplyPrompt(options) {
1039
+ const common = this.runtimePromptCommon(options);
1040
+ const created = {
1041
+ ...common,
1042
+ kind: 'new_course_runtime_reply',
1043
+ tellaskReplyDirective: options.tellaskReplyDirective,
1044
+ skipTaskdoc: options.skipTaskdoc,
1045
+ };
1046
+ this.enqueueQueuedPromptState(created);
1047
+ await this.persistPendingRuntimePrompt({
1048
+ content: created.prompt,
1049
+ msgId: created.msgId,
1050
+ grammar: created.grammar ?? 'markdown',
1051
+ userLanguageCode: created.userLanguageCode,
1052
+ origin: 'runtime',
1053
+ tellaskReplyDirective: created.tellaskReplyDirective,
1054
+ skipTaskdoc: created.skipTaskdoc,
1055
+ });
1056
+ return created;
1057
+ }
1058
+ async queueRuntimeSideDialogPrompt(options) {
1059
+ const common = this.runtimePromptCommon(options);
1060
+ const created = {
1061
+ ...common,
1062
+ kind: 'new_course_runtime_sideDialog',
1063
+ tellaskReplyDirective: options.tellaskReplyDirective,
1064
+ skipTaskdoc: options.skipTaskdoc,
1065
+ sideDialogReplyTarget: options.sideDialogReplyTarget,
1066
+ };
1067
+ this.enqueueQueuedPromptState(created);
1068
+ await this.persistPendingRuntimePrompt({
1069
+ content: created.prompt,
1070
+ msgId: created.msgId,
1071
+ grammar: created.grammar ?? 'markdown',
1072
+ userLanguageCode: created.userLanguageCode,
1073
+ origin: 'runtime',
1074
+ tellaskReplyDirective: created.tellaskReplyDirective,
1075
+ skipTaskdoc: created.skipTaskdoc,
1076
+ sideDialogReplyTarget: created.sideDialogReplyTarget,
1077
+ });
1078
+ return created;
1079
+ }
1021
1080
  hasUpNext() {
1022
1081
  return this._upNextQueue.length > 0;
1023
1082
  }
@@ -1905,6 +1964,7 @@ class DialogStore {
1905
1964
  * Start a new course in storage
1906
1965
  */
1907
1966
  async startNewCourse(_dialog, _newCoursePrompt) { }
1967
+ async persistPendingRuntimePrompt(_dialog, _prompt) { }
1908
1968
  /**
1909
1969
  * Handle stream error
1910
1970
  */
@@ -581,6 +581,7 @@ providers:
581
581
  context_window: '204K'
582
582
  glm-5.1:
583
583
  name: GLM-5.1
584
+ optimal_max_tokens: 180000
584
585
  supports_thinking: true
585
586
  default_thinking: true
586
587
  supports_image_input: false
@@ -590,6 +591,7 @@ providers:
590
591
  context_window: '256K'
591
592
  glm-4.7:
592
593
  name: GLM-4.7
594
+ optimal_max_tokens: 180000
593
595
  supports_thinking: true
594
596
  default_thinking: false
595
597
  supports_image_input: false
@@ -1955,9 +1955,13 @@ class AnthropicGen {
1955
1955
  completionTokens,
1956
1956
  totalTokens: promptTokens + completionTokens,
1957
1957
  };
1958
+ const messages = anthropicToChatMessages(response, genseq, forceJsonResponse ? ANTHROPIC_JSON_RESPONSE_TOOL_NAME : undefined);
1959
+ const orderedOutputs = outputs.length > 0
1960
+ ? [...outputs, ...messages.map((message) => ({ kind: 'message', message }))]
1961
+ : [];
1958
1962
  return {
1959
- messages: anthropicToChatMessages(response, genseq, forceJsonResponse ? ANTHROPIC_JSON_RESPONSE_TOOL_NAME : undefined),
1960
- ...(outputs.length > 0 ? { outputs } : {}),
1963
+ messages,
1964
+ ...(orderedOutputs.length > 0 ? { outputs: orderedOutputs } : {}),
1961
1965
  usage,
1962
1966
  llmGenModel: returnedModel,
1963
1967
  };
@@ -474,6 +474,10 @@ responses:
474
474
  : `mock_func_${String(i + 1)}_${call.name}`;
475
475
  await receiver.funcCall(callId, call.name, this.normalizeFuncCallArgs(call.arguments));
476
476
  }
477
+ const invalidFuncCalls = matched?.invalidFuncCalls ?? [];
478
+ for (const call of invalidFuncCalls) {
479
+ await receiver.invalidFuncCall?.(call);
480
+ }
477
481
  return { usage, llmGenModel: modelName };
478
482
  }
479
483
  async genMoreMessages(providerConfig, agent, systemPrompt, _funcTools, _requestContext, context, genseq, abortSignal) {
@@ -575,14 +579,28 @@ responses:
575
579
  arguments: this.normalizeFuncCallArgs(call.arguments),
576
580
  };
577
581
  }) ?? [];
582
+ const invalidFuncCalls = matched?.invalidFuncCalls ?? [];
583
+ const invalidFuncCallOutputs = invalidFuncCalls.map((call) => ({
584
+ kind: 'invalid_func_call',
585
+ call,
586
+ }));
587
+ const messages = thinking !== undefined
588
+ ? saying
589
+ ? [thinking, saying, ...funcMsgs]
590
+ : [thinking, ...funcMsgs]
591
+ : saying
592
+ ? [saying, ...funcMsgs]
593
+ : [...funcMsgs];
578
594
  return {
579
- messages: thinking !== undefined
580
- ? saying
581
- ? [thinking, saying, ...funcMsgs]
582
- : [thinking, ...funcMsgs]
583
- : saying
584
- ? [saying, ...funcMsgs]
585
- : [...funcMsgs],
595
+ messages,
596
+ ...(invalidFuncCallOutputs.length > 0
597
+ ? {
598
+ outputs: [
599
+ ...messages.map((message) => ({ kind: 'message', message })),
600
+ ...invalidFuncCallOutputs,
601
+ ],
602
+ }
603
+ : {}),
586
604
  usage,
587
605
  llmGenModel: modelName,
588
606
  };
@@ -14,7 +14,7 @@ import type { ChatCompletion, ChatCompletionChunk, ChatCompletionMessageParam }
14
14
  import type { Team } from '../../team';
15
15
  import type { FuncTool } from '../../tool';
16
16
  import type { ChatMessage, ModelInfo, ProviderConfig } from '../client';
17
- import { type LlmBatchResult, type LlmFailureDisposition, type LlmGenerator, type LlmRequestContext, type LlmStreamReceiver, type LlmStreamResult } from '../gen';
17
+ import { type LlmBatchOutput, type LlmBatchResult, type LlmFailureDisposition, type LlmGenerator, type LlmRequestContext, type LlmStreamReceiver, type LlmStreamResult } from '../gen';
18
18
  type OpenAiCompatibleChatExtraParams = {
19
19
  thinking?: boolean | Record<string, unknown>;
20
20
  reasoning_effort?: NonNullable<Team.ModelParams['openai-compatible']>['reasoning_effort'];
@@ -48,6 +48,7 @@ export declare function consumeOpenAiCompatibleChatCompletionStreamForTest(args:
48
48
  abortSignal?: AbortSignal;
49
49
  }): Promise<LlmStreamResult>;
50
50
  export declare function chatCompletionToChatMessagesForTest(response: ChatCompletion, genseq: number): ChatMessage[];
51
+ export declare function chatCompletionToBatchOutputsForTest(response: ChatCompletion, genseq: number): LlmBatchOutput[];
51
52
  export declare class OpenAiCompatibleGen implements LlmGenerator {
52
53
  get apiType(): string;
53
54
  classifyFailure(error: unknown): LlmFailureDisposition | undefined;
@@ -22,6 +22,7 @@ exports.wrapOpenAiCompatibleRejectedRequestErrorForTest = wrapOpenAiCompatibleRe
22
22
  exports.buildOpenAiCompatibleRequestMessagesWrapper = buildOpenAiCompatibleRequestMessagesWrapper;
23
23
  exports.consumeOpenAiCompatibleChatCompletionStreamForTest = consumeOpenAiCompatibleChatCompletionStreamForTest;
24
24
  exports.chatCompletionToChatMessagesForTest = chatCompletionToChatMessagesForTest;
25
+ exports.chatCompletionToBatchOutputsForTest = chatCompletionToBatchOutputsForTest;
25
26
  const events_1 = require("events");
26
27
  const fs_1 = require("fs");
27
28
  const promises_1 = __importDefault(require("fs/promises"));
@@ -1475,6 +1476,24 @@ function throwOpenAiCompatibleMalformedBatchToolCall(detail) {
1475
1476
  log.error(message, error);
1476
1477
  throw error;
1477
1478
  }
1479
+ function buildInvalidStreamedToolFunctionNameCall(state) {
1480
+ return {
1481
+ provider: 'openai-compatible',
1482
+ callId: state.callId,
1483
+ detail: `OPENAI-COMPATIBLE missing streamed tool function name for callId=${state.callId}`,
1484
+ toolCallIndex: state.index,
1485
+ rawArgumentsText: state.argsJson,
1486
+ };
1487
+ }
1488
+ function buildInvalidToolFunctionNameCall(args) {
1489
+ return {
1490
+ provider: 'openai-compatible',
1491
+ callId: args.callId,
1492
+ detail: `OPENAI-COMPATIBLE missing tool function name for callId=${args.callId}`,
1493
+ toolCallIndex: args.toolCallIndex,
1494
+ rawArgumentsText: args.rawArgumentsText,
1495
+ };
1496
+ }
1478
1497
  async function maybeEmitFuncCall(state, receiver, genseq) {
1479
1498
  if (state.emitted)
1480
1499
  return;
@@ -1482,17 +1501,32 @@ async function maybeEmitFuncCall(state, receiver, genseq) {
1482
1501
  state.callId = synthesizeCallId(genseq, state.index);
1483
1502
  }
1484
1503
  if (state.name.trim().length === 0) {
1485
- const detail = `OPENAI-COMPATIBLE missing streamed tool function name for callId=${state.callId}`;
1504
+ if (state.argsJson.trim().length === 0) {
1505
+ log.warn('OPENAI-COMPATIBLE ignored empty streamed tool call placeholder', undefined, {
1506
+ callId: state.callId,
1507
+ index: state.index,
1508
+ });
1509
+ state.emitted = true;
1510
+ return;
1511
+ }
1512
+ const invalidCall = buildInvalidStreamedToolFunctionNameCall(state);
1513
+ const detail = invalidCall.detail;
1486
1514
  log.error(detail, new Error('openai_compatible_missing_tool_call_name'), {
1487
1515
  callId: state.callId,
1516
+ index: state.index,
1488
1517
  });
1489
- if (receiver.streamError) {
1490
- await receiver.streamError(detail);
1518
+ if (!receiver.invalidFuncCall) {
1519
+ if (receiver.streamError) {
1520
+ await receiver.streamError(detail);
1521
+ }
1522
+ throw buildOpenAiCompatibleStreamError({
1523
+ detail,
1524
+ kind: 'invalid_tool_call',
1525
+ });
1491
1526
  }
1492
- throw buildOpenAiCompatibleStreamError({
1493
- detail,
1494
- kind: 'invalid_tool_call',
1495
- });
1527
+ state.emitted = true;
1528
+ await receiver.invalidFuncCall(invalidCall);
1529
+ return;
1496
1530
  }
1497
1531
  state.emitted = true;
1498
1532
  const args = state.argsJson.trim().length > 0 ? state.argsJson : '{}';
@@ -1694,25 +1728,27 @@ async function consumeOpenAiCompatibleChatCompletionStreamForTest(args) {
1694
1728
  abortSignal: args.abortSignal,
1695
1729
  });
1696
1730
  }
1697
- function chatCompletionToChatMessages(response, genseq) {
1698
- const out = [];
1731
+ function chatCompletionToBatchOutputs(response, genseq) {
1732
+ const outputs = [];
1699
1733
  const choice = response.choices && response.choices.length > 0 ? response.choices[0] : undefined;
1700
1734
  const msg = choice ? choice.message : undefined;
1701
1735
  if (!msg)
1702
- return out;
1736
+ return outputs;
1703
1737
  const reasoningContent = extractReasoningContentField(msg);
1704
1738
  if (reasoningContent && reasoningContent.length > 0) {
1705
- out.push({
1739
+ const message = {
1706
1740
  type: 'thinking_msg',
1707
1741
  role: 'assistant',
1708
1742
  genseq,
1709
1743
  content: reasoningContent,
1710
1744
  reasoning: buildReasoningPayloadFromText(reasoningContent),
1711
- });
1745
+ };
1746
+ outputs.push({ kind: 'message', message });
1712
1747
  }
1713
1748
  const content = typeof msg.content === 'string' ? msg.content : null;
1714
1749
  if (content && content.length > 0) {
1715
- out.push({ type: 'saying_msg', role: 'assistant', genseq, content });
1750
+ const message = { type: 'saying_msg', role: 'assistant', genseq, content };
1751
+ outputs.push({ kind: 'message', message });
1716
1752
  }
1717
1753
  const toolCalls = msg.tool_calls;
1718
1754
  if (Array.isArray(toolCalls)) {
@@ -1730,23 +1766,48 @@ function chatCompletionToChatMessages(response, genseq) {
1730
1766
  const name = typeof call.function?.name === 'string' ? call.function.name : '';
1731
1767
  const args = typeof call.function?.arguments === 'string' ? call.function.arguments : '';
1732
1768
  if (name.trim().length === 0) {
1733
- throwOpenAiCompatibleMalformedBatchToolCall(`missing tool function name for callId=${callId}`);
1769
+ const invalidCall = buildInvalidToolFunctionNameCall({
1770
+ callId,
1771
+ toolCallIndex: index,
1772
+ rawArgumentsText: args,
1773
+ });
1774
+ log.error(invalidCall.detail, new Error('openai_compatible_missing_tool_call_name'), {
1775
+ callId,
1776
+ index,
1777
+ });
1778
+ outputs.push({ kind: 'invalid_func_call', call: invalidCall });
1779
+ continue;
1734
1780
  }
1735
- out.push({
1781
+ const message = {
1736
1782
  type: 'func_call_msg',
1737
1783
  role: 'assistant',
1738
1784
  genseq,
1739
1785
  id: callId,
1740
1786
  name,
1741
1787
  arguments: args,
1742
- });
1788
+ };
1789
+ outputs.push({ kind: 'message', message });
1743
1790
  }
1744
1791
  }
1792
+ return outputs;
1793
+ }
1794
+ function batchOutputsToChatMessages(outputs) {
1795
+ return outputs
1796
+ .filter((output) => {
1797
+ return output.kind === 'message';
1798
+ })
1799
+ .map((output) => output.message);
1800
+ }
1801
+ function chatCompletionToChatMessages(response, genseq) {
1802
+ const out = batchOutputsToChatMessages(chatCompletionToBatchOutputs(response, genseq));
1745
1803
  return out;
1746
1804
  }
1747
1805
  function chatCompletionToChatMessagesForTest(response, genseq) {
1748
1806
  return chatCompletionToChatMessages(response, genseq);
1749
1807
  }
1808
+ function chatCompletionToBatchOutputsForTest(response, genseq) {
1809
+ return chatCompletionToBatchOutputs(response, genseq);
1810
+ }
1750
1811
  class OpenAiCompatibleGen {
1751
1812
  get apiType() {
1752
1813
  return 'openai-compatible';
@@ -1898,7 +1959,15 @@ class OpenAiCompatibleGen {
1898
1959
  const response = await client.chat.completions.create(payload, {
1899
1960
  ...(abortSignal ? { signal: abortSignal } : {}),
1900
1961
  });
1901
- const messagesOut = chatCompletionToChatMessages(response, genseq);
1962
+ const batchOutputs = chatCompletionToBatchOutputs(response, genseq);
1963
+ const messagesOut = batchOutputsToChatMessages(batchOutputs);
1964
+ const orderedOutputs = outputs.length > 0
1965
+ ? [
1966
+ ...outputs,
1967
+ ...messagesOut.map((message) => ({ kind: 'message', message })),
1968
+ ...batchOutputs.filter((output) => output.kind !== 'message'),
1969
+ ]
1970
+ : batchOutputs;
1902
1971
  const usage = response.usage
1903
1972
  ? tryExtractChatUsage(response.usage)
1904
1973
  : { kind: 'unavailable' };
@@ -1907,7 +1976,7 @@ class OpenAiCompatibleGen {
1907
1976
  : undefined;
1908
1977
  return {
1909
1978
  messages: messagesOut,
1910
- ...(outputs.length > 0 ? { outputs } : {}),
1979
+ ...(orderedOutputs.length > 0 ? { outputs: orderedOutputs } : {}),
1911
1980
  usage,
1912
1981
  ...(model ? { llmGenModel: model } : {}),
1913
1982
  };
package/dist/llm/gen.d.ts CHANGED
@@ -20,6 +20,9 @@ export declare class LlmStreamErrorEmittedError extends Error {
20
20
  export type LlmBatchOutput = {
21
21
  kind: 'message';
22
22
  message: ChatMessage;
23
+ } | {
24
+ kind: 'invalid_func_call';
25
+ call: LlmInvalidFuncCall;
23
26
  } | {
24
27
  kind: 'web_search_call';
25
28
  call: LlmWebSearchCall;
@@ -138,6 +141,14 @@ export type OpenAiResponsesCustomToolCall = {
138
141
  detail?: string;
139
142
  };
140
143
  export type OpenAiResponsesNativeToolCall = OpenAiResponsesNonCustomNativeToolCall | OpenAiResponsesCustomToolCall;
144
+ export type LlmInvalidFuncCall = Readonly<{
145
+ provider: string;
146
+ callId: string;
147
+ detail: string;
148
+ toolCallIndex?: number;
149
+ rawFunctionName?: string;
150
+ rawArgumentsText?: string;
151
+ }>;
141
152
  export interface LlmStreamReceiver {
142
153
  thinkingStart: () => Promise<void>;
143
154
  thinkingChunk: (chunk: string) => Promise<void>;
@@ -149,6 +160,7 @@ export interface LlmStreamReceiver {
149
160
  rawCallId?: string;
150
161
  effectiveCallId?: string;
151
162
  }) => Promise<void>;
163
+ invalidFuncCall?: (call: LlmInvalidFuncCall) => Promise<void>;
152
164
  webSearchCall?: (call: LlmWebSearchCall) => Promise<void>;
153
165
  nativeToolCall?: (call: OpenAiResponsesNativeToolCall) => Promise<void>;
154
166
  toolResultImageIngest?: (ingest: ToolResultImageIngest) => Promise<void>;
@@ -205,6 +205,9 @@ async function persistAndEmitRuntimeGuide(dlg, content) {
205
205
  role: 'assistant',
206
206
  content,
207
207
  });
208
+ await persistAndPostRuntimeGuide(dlg, content);
209
+ }
210
+ async function persistAndPostRuntimeGuide(dlg, content) {
208
211
  await persistence_1.DialogPersistence.persistRuntimeGuide(dlg, content, dlg.activeGenSeq);
209
212
  (0, evt_registry_1.postDialogEvent)(dlg, {
210
213
  type: 'runtime_guide_evt',
@@ -301,6 +304,9 @@ function hasMeaningfulBatchOutput(batch) {
301
304
  if (output.kind === 'user_image_ingest') {
302
305
  continue;
303
306
  }
307
+ if (output.kind === 'invalid_func_call') {
308
+ return true;
309
+ }
304
310
  if (output.kind !== 'message') {
305
311
  return true;
306
312
  }
@@ -910,6 +916,75 @@ async function emitAssistantSaying(dlg, content) {
910
916
  await dlg.sayingChunk(content);
911
917
  await dlg.sayingFinish();
912
918
  }
919
+ function formatInvalidFuncCallRuntimeGuide(language, call) {
920
+ const rawName = call.rawFunctionName !== undefined && call.rawFunctionName.trim() !== ''
921
+ ? call.rawFunctionName.trim()
922
+ : '<missing>';
923
+ const rawArguments = call.rawArgumentsText !== undefined && call.rawArgumentsText.trim() !== ''
924
+ ? call.rawArgumentsText
925
+ : '<empty>';
926
+ const indexLine = call.toolCallIndex === undefined ? undefined : `- toolCallIndex: ${String(call.toolCallIndex)}`;
927
+ if (language === 'en') {
928
+ return [
929
+ '[Runtime notice] The previous model output contained an invalid tool-call payload that could not be represented as a normal provider tool call in the next generation context.',
930
+ '',
931
+ `- provider: ${call.provider}`,
932
+ `- callId: ${call.callId}`,
933
+ `- problem: ${call.detail}`,
934
+ `- rawFunctionName: ${rawName}`,
935
+ `- rawArgumentsText:`,
936
+ '```json',
937
+ rawArguments,
938
+ '```',
939
+ ...(indexLine === undefined ? [] : [indexLine]),
940
+ '',
941
+ 'Treat that payload as failed. Do not assume the tool ran. Continue from the current task, and if a tool call is still needed, emit a new valid tool call with a non-empty function name and valid arguments.',
942
+ ]
943
+ .filter((line) => line.length > 0)
944
+ .join('\n');
945
+ }
946
+ return [
947
+ '[运行时提示] 上一轮模型输出包含一个无效工具调用载荷,无法按正常 provider tool call 形态进入下一轮生成上下文。',
948
+ '',
949
+ `- provider: ${call.provider}`,
950
+ `- callId: ${call.callId}`,
951
+ `- 问题: ${call.detail}`,
952
+ `- rawFunctionName: ${rawName}`,
953
+ `- rawArgumentsText:`,
954
+ '```json',
955
+ rawArguments,
956
+ '```',
957
+ ...(indexLine === undefined ? [] : [indexLine]),
958
+ '',
959
+ '请把该载荷视为调用失败,不要假设工具已经执行。继续当前任务;如果仍需要调用工具,请重新发起一个函数名非空、参数有效的新工具调用。',
960
+ ]
961
+ .filter((line) => line.length > 0)
962
+ .join('\n');
963
+ }
964
+ async function persistInvalidFuncCallRuntimeGuide(args) {
965
+ const { dlg, call } = args;
966
+ const sourceText = args.source === 'streamed' ? 'streamed' : 'batch';
967
+ log_1.log.error(`kernel-driver received invalid ${sourceText} function call payload`, new Error(`kernel_driver_invalid_${sourceText}_function_call_payload`), {
968
+ rootId: dlg.id.rootId,
969
+ selfId: dlg.id.selfId,
970
+ course: dlg.activeGenCourseOrUndefined ?? dlg.currentCourse,
971
+ genseq: dlg.activeGenSeq,
972
+ callId: call.callId,
973
+ provider: call.provider,
974
+ detail: call.detail,
975
+ toolCallIndex: call.toolCallIndex,
976
+ });
977
+ if (args.emitStreamError) {
978
+ await dlg.streamError(call.detail);
979
+ }
980
+ const content = formatInvalidFuncCallRuntimeGuide((0, work_language_1.getWorkLanguage)(), call);
981
+ args.newMsgs.push({
982
+ type: 'transient_guide_msg',
983
+ role: 'assistant',
984
+ content,
985
+ });
986
+ await persistAndPostRuntimeGuide(dlg, content);
987
+ }
913
988
  function resolveFuncToolFollowupMode(tool) {
914
989
  return tool?.followupMode ?? 'immediate';
915
990
  }
@@ -1895,6 +1970,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1895
1970
  const streamedFuncCalls = [];
1896
1971
  let sawWebSearchSideChannelOutput = false;
1897
1972
  let sawNativeToolSideChannelOutput = false;
1973
+ let invalidFuncCallCount = 0;
1898
1974
  const streamOrBatch = async () => {
1899
1975
  let batchAttemptCourse;
1900
1976
  let batchAttemptCheckpointOffset;
@@ -1911,6 +1987,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
1911
1987
  });
1912
1988
  sawWebSearchSideChannelOutput = false;
1913
1989
  sawNativeToolSideChannelOutput = false;
1990
+ invalidFuncCallCount = 0;
1914
1991
  streamedFuncCalls.length = 0;
1915
1992
  newMsgs.length = 0;
1916
1993
  };
@@ -2004,6 +2081,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2004
2081
  sawWebSearchSideChannelOutput = false;
2005
2082
  sawNativeToolSideChannelOutput = false;
2006
2083
  streamedFuncCalls.length = 0;
2084
+ invalidFuncCallCount = 0;
2007
2085
  newMsgs.length = 0;
2008
2086
  };
2009
2087
  const receiver = {
@@ -2127,6 +2205,17 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2127
2205
  arguments: argsStr,
2128
2206
  });
2129
2207
  },
2208
+ invalidFuncCall: async (call) => {
2209
+ throwIfAborted(abortSignal, dlg);
2210
+ invalidFuncCallCount += 1;
2211
+ await persistInvalidFuncCallRuntimeGuide({
2212
+ dlg,
2213
+ call,
2214
+ source: 'streamed',
2215
+ newMsgs,
2216
+ emitStreamError: true,
2217
+ });
2218
+ },
2130
2219
  webSearchCall: async (call) => {
2131
2220
  throwIfAborted(abortSignal, dlg);
2132
2221
  sawWebSearchSideChannelOutput = true;
@@ -2177,6 +2266,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2177
2266
  sawWebSearchSideChannelOutput = false;
2178
2267
  sawNativeToolSideChannelOutput = false;
2179
2268
  streamedFuncCalls.length = 0;
2269
+ invalidFuncCallCount = 0;
2180
2270
  newMsgs.length = 0;
2181
2271
  const promptCacheKey = prepareLlmRequestContextKey();
2182
2272
  const streamResult = await llmGen.genToReceiver(providerCfg, agent, systemPrompt, funcTools, {
@@ -2192,6 +2282,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2192
2282
  const hasFunctionCall = streamedFuncCalls.length > 0;
2193
2283
  if (!hasFinishedMessageContent &&
2194
2284
  !hasFunctionCall &&
2285
+ invalidFuncCallCount === 0 &&
2195
2286
  !sawWebSearchSideChannelOutput &&
2196
2287
  !sawNativeToolSideChannelOutput) {
2197
2288
  throw {
@@ -2259,6 +2350,17 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2259
2350
  }
2260
2351
  break;
2261
2352
  }
2353
+ case 'invalid_func_call': {
2354
+ invalidFuncCallCount += 1;
2355
+ await persistInvalidFuncCallRuntimeGuide({
2356
+ dlg,
2357
+ call: output.call,
2358
+ source: 'batch',
2359
+ newMsgs,
2360
+ emitStreamError: true,
2361
+ });
2362
+ break;
2363
+ }
2262
2364
  case 'web_search_call': {
2263
2365
  sawWebSearchSideChannelOutput = true;
2264
2366
  await dlg.webSearchCall(projectLlmWebSearchCall(output.call));
@@ -2468,7 +2570,9 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
2468
2570
  // warrant same-drive LLM reaction right away. Provider-native side-channel UI events are
2469
2571
  // meaningful output, but they are not transcript/context inputs and therefore must not
2470
2572
  // trigger another immediate generation round by themselves.
2471
- const shouldStartImmediatePostToolGeneration = routed.hasImmediateFollowupToolCalls || routed.tellaskToolOutputs.length > 0;
2573
+ const shouldStartImmediatePostToolGeneration = routed.hasImmediateFollowupToolCalls ||
2574
+ routed.tellaskToolOutputs.length > 0 ||
2575
+ invalidFuncCallCount > 0;
2472
2576
  if (!shouldStartImmediatePostToolGeneration) {
2473
2577
  const healthFirst = await maybeContinueWithHealthPromptBeforeDiligence({
2474
2578
  dlg,
@@ -23,6 +23,28 @@ const idle_reminder_wake_1 = require("./idle-reminder-wake");
23
23
  const reply_guidance_1 = require("./reply-guidance");
24
24
  const sideDialog_1 = require("./sideDialog");
25
25
  const tellask_special_1 = require("./tellask-special");
26
+ async function queueReplyReminderFollowUp(args) {
27
+ if (args.followUp.kind === 'runtime_sideDialog_reply_reminder') {
28
+ await args.dialog.queueRuntimeSideDialogPrompt({
29
+ prompt: args.followUp.prompt,
30
+ msgId: args.followUp.msgId,
31
+ grammar: args.followUp.grammar ?? 'markdown',
32
+ userLanguageCode: args.followUp.userLanguageCode,
33
+ tellaskReplyDirective: args.followUp.tellaskReplyDirective,
34
+ skipTaskdoc: args.followUp.skipTaskdoc,
35
+ sideDialogReplyTarget: args.followUp.sideDialogReplyTarget,
36
+ });
37
+ return;
38
+ }
39
+ await args.dialog.queueRuntimeReplyPrompt({
40
+ prompt: args.followUp.prompt,
41
+ msgId: args.followUp.msgId,
42
+ grammar: args.followUp.grammar ?? 'markdown',
43
+ userLanguageCode: args.followUp.userLanguageCode,
44
+ tellaskReplyDirective: args.followUp.tellaskReplyDirective,
45
+ skipTaskdoc: args.followUp.skipTaskdoc,
46
+ });
47
+ }
26
48
  function isReplyToolReminderPrompt(prompt) {
27
49
  return typeof prompt?.content === 'string' && (0, reply_prompt_copy_1.isReplyToolReminderPromptContent)(prompt.content);
28
50
  }
@@ -1084,6 +1106,18 @@ async function executeDriveRound(args) {
1084
1106
  }
1085
1107
  }
1086
1108
  if (followUp) {
1109
+ if (followUp.kind === 'runtime_reply_reminder' ||
1110
+ followUp.kind === 'runtime_sideDialog_reply_reminder') {
1111
+ await queueReplyReminderFollowUp({ dialog, followUp });
1112
+ args.scheduleDrive(dialog, {
1113
+ waitInQue: true,
1114
+ driveOptions: {
1115
+ source: 'kernel_driver_follow_up',
1116
+ reason: 'follow_up_prompt',
1117
+ },
1118
+ });
1119
+ return driveResult;
1120
+ }
1087
1121
  args.scheduleDrive(dialog, {
1088
1122
  waitInQue: true,
1089
1123
  driveOptions: {
@@ -1116,9 +1150,7 @@ async function executeDriveRound(args) {
1116
1150
  case 'registered_assignment_update':
1117
1151
  case 'new_course_runtime_guide':
1118
1152
  case 'new_course_runtime_reply':
1119
- case 'new_course_runtime_sideDialog':
1120
- case 'runtime_reply_reminder':
1121
- case 'runtime_sideDialog_reply_reminder': {
1153
+ case 'new_course_runtime_sideDialog': {
1122
1154
  const runtimeCommon = {
1123
1155
  ...common,
1124
1156
  origin: 'runtime',
@@ -1127,8 +1159,7 @@ async function executeDriveRound(args) {
1127
1159
  : { skipTaskdoc: followUp.skipTaskdoc }),
1128
1160
  };
1129
1161
  if (followUp.kind === 'registered_assignment_update' ||
1130
- followUp.kind === 'new_course_runtime_sideDialog' ||
1131
- followUp.kind === 'runtime_sideDialog_reply_reminder') {
1162
+ followUp.kind === 'new_course_runtime_sideDialog') {
1132
1163
  const prompt = {
1133
1164
  ...runtimeCommon,
1134
1165
  tellaskReplyDirective: followUp.tellaskReplyDirective,
@@ -1136,8 +1167,7 @@ async function executeDriveRound(args) {
1136
1167
  };
1137
1168
  return prompt;
1138
1169
  }
1139
- if (followUp.kind === 'new_course_runtime_reply' ||
1140
- followUp.kind === 'runtime_reply_reminder') {
1170
+ if (followUp.kind === 'new_course_runtime_reply') {
1141
1171
  const prompt = {
1142
1172
  ...runtimeCommon,
1143
1173
  tellaskReplyDirective: followUp.tellaskReplyDirective,
package/dist/log.js CHANGED
@@ -78,6 +78,39 @@ const DETAIL_INSPECT_PROFILES = [
78
78
  maxStringLength: 192,
79
79
  },
80
80
  ];
81
+ const LOG_KEY_PRIORITY = new Map([
82
+ 'rootId',
83
+ 'selfId',
84
+ 'dialogId',
85
+ 'course',
86
+ 'genseq',
87
+ 'callId',
88
+ 'questionId',
89
+ 'agentId',
90
+ 'toolName',
91
+ 'provider',
92
+ 'model',
93
+ 'dialogClass',
94
+ 'status',
95
+ 'error',
96
+ 'detail',
97
+ 'message',
98
+ 'code',
99
+ 'name',
100
+ 'type',
101
+ ].map((key, index) => [key, index]));
102
+ function compareLogObjectKeys(left, right) {
103
+ const leftPriority = LOG_KEY_PRIORITY.get(left);
104
+ const rightPriority = LOG_KEY_PRIORITY.get(right);
105
+ if (leftPriority !== undefined || rightPriority !== undefined) {
106
+ return (leftPriority ?? Number.MAX_SAFE_INTEGER) - (rightPriority ?? Number.MAX_SAFE_INTEGER);
107
+ }
108
+ const leftInternal = left.startsWith('_') || left.startsWith('__');
109
+ const rightInternal = right.startsWith('_') || right.startsWith('__');
110
+ if (leftInternal !== rightInternal)
111
+ return leftInternal ? 1 : -1;
112
+ return left.localeCompare(right);
113
+ }
81
114
  function truncateText(value, maxChars, suffix) {
82
115
  if (maxChars <= 0) {
83
116
  return { text: '', truncated: value.length > 0 };
@@ -248,7 +281,7 @@ function pruneForLog(value, profile, depth, seen) {
248
281
  return { value: `[${ctorName} byteLength=${byteLen}]`, pruned: true };
249
282
  }
250
283
  const source = value;
251
- const allKeys = Object.keys(source).sort();
284
+ const allKeys = Object.keys(source).sort(compareLogObjectKeys);
252
285
  const visibleKeys = allKeys.slice(0, profile.maxObjectKeys);
253
286
  const ctorName = getConstructorName(value);
254
287
  const reduced = {};
@@ -286,7 +319,7 @@ function inspectAdaptive(value, maxChars) {
286
319
  depth: null,
287
320
  breakLength: 120,
288
321
  compact: false,
289
- sorted: true,
322
+ sorted: false,
290
323
  maxArrayLength: profile.maxArrayItems,
291
324
  maxStringLength: profile.maxStringLength,
292
325
  });
@@ -131,6 +131,7 @@ export declare class DiskFileDialogStore extends DialogStore {
131
131
  * Start new course (append-only JSONL + exceptional reminder persistence)
132
132
  */
133
133
  startNewCourse(dialog: Dialog, newCoursePrompt: DialogRuntimePrompt): Promise<void>;
134
+ persistPendingRuntimePrompt(dialog: Dialog, prompt: DialogRuntimePrompt): Promise<void>;
134
135
  /**
135
136
  * Persist reminder state (exceptional overwrite pattern)
136
137
  * Note: Event emission is handled by processReminderUpdates() in Dialog
@@ -2573,9 +2573,20 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2573
2573
  * Emit stream error for current generation lifecycle (uses active genseq when present)
2574
2574
  */
2575
2575
  async streamError(dialog, error) {
2576
- log_1.log.error(`Dialog stream error '${error}'`, new Error(), { dialog });
2577
2576
  const course = dialog.activeGenCourseOrUndefined ?? dialog.currentCourse;
2578
2577
  const genseq = dialog.activeGenSeqOrUndefined;
2578
+ log_1.log.error(`Dialog stream error '${error}'`, new Error(), {
2579
+ dialogId: dialog.id.valueOf(),
2580
+ rootId: dialog.id.rootId,
2581
+ selfId: dialog.id.selfId,
2582
+ course,
2583
+ genseq,
2584
+ agentId: dialog.agentId,
2585
+ dialogClass: dialog.constructor.name,
2586
+ status: dialog.status,
2587
+ activeGeneration: dialog.hasActiveGeneration,
2588
+ dialogSnapshot: dialog,
2589
+ });
2579
2590
  // Enhanced stream error event with better error classification
2580
2591
  const streamErrorEvent = {
2581
2592
  type: 'stream_error_evt',
@@ -2628,6 +2639,18 @@ class DiskFileDialogStore extends dialog_1.DialogStore {
2628
2639
  };
2629
2640
  (0, evt_registry_1.postDialogEvent)(dialog, courseUpdateEvt);
2630
2641
  }
2642
+ async persistPendingRuntimePrompt(dialog, prompt) {
2643
+ if (prompt.origin !== 'runtime') {
2644
+ throw new Error(`persistPendingRuntimePrompt invariant violation: pending prompt must have runtime origin for dialog=${dialog.id.valueOf()}`);
2645
+ }
2646
+ await DialogPersistence.mutateDialogLatest(dialog.id, () => ({
2647
+ kind: 'patch',
2648
+ patch: {
2649
+ needsDrive: true,
2650
+ pendingCourseStartPrompt: prompt,
2651
+ },
2652
+ }), dialog.status);
2653
+ }
2631
2654
  /**
2632
2655
  * Persist reminder state (exceptional overwrite pattern)
2633
2656
  * Note: Event emission is handled by processReminderUpdates() in Dialog
@@ -217,14 +217,14 @@ function formatReminderContextGuide(language) {
217
217
  if (language === 'zh') {
218
218
  return [
219
219
  `${formatSystemNoticePrefix(language)} 提醒项上下文块开始`,
220
- '以下是当前可见提醒项的运行时上下文投影。由于当前 LLM Provider 通常不支持 role=environment,Dominds 默认把系统运行时提醒包装投影为 role=user;个别提醒项可由其 owner 按自身契约选择 role。无论最终 role 如何,它们都不是新的用户指令/诉求,也不是聊天正文。',
220
+ '以下是当前可见提醒项的运行时上下文投影。由于当前 LLM Provider 通常不支持 role=environment,Dominds 默认把系统运行时提醒包装投影为 role=user;个别提醒项可由其 owner 按自身契约选择 role。无论最终 role 如何,它们都不是用户的新诉求/指令,也不是聊天正文。',
221
221
  '在 WebUI 中,用户通过独立的 Reminder 小组件/面板项看到这些提醒,并能把它们和聊天正文区分开。',
222
222
  '请把提醒项作为工作集/状态参考;只有实际改变你的判断、计划或风险的信息,才需要提炼进后续有实质内容的对外回复。不要为了提醒项单独回复“收到/已了解/静默吸收”。',
223
223
  ].join('\n');
224
224
  }
225
225
  return [
226
226
  `${formatSystemNoticePrefix(language)} Reminder context block begins`,
227
- 'The following visible reminders are runtime-added context projections. Because current LLM providers usually do not support role=environment, Dominds projects default system-runtime reminder wrappers as role=user; individual reminder owners may choose the role required by their own contract. Regardless of their final role, these reminders are not new user instructions or requests, and not chat transcript text.',
227
+ 'The following visible reminders are runtime-added context projections. Because current LLM providers usually do not support role=environment, Dominds projects default system-runtime reminder wrappers as role=user; individual reminder owners may choose the role required by their own contract. Regardless of their final role, these reminders are not new user requests/instructions, and not chat transcript text.',
228
228
  'In the WebUI, the user sees these reminders through a separate Reminder widget/panel item and can distinguish them from the chat transcript.',
229
229
  'Use reminders as workset/state references; only carry information into a later substantive outward reply when it materially changes your current judgment, plan, or risk. Do not send a standalone "acknowledged/noted/silently absorbed" reply for reminder items.',
230
230
  ].join('\n');
@@ -234,23 +234,27 @@ function formatReminderItemProjectionNote(language) {
234
234
  }
235
235
  function formatReminderContextFooter(language, followingState) {
236
236
  if (language === 'zh') {
237
- const base = `${formatSystemNoticePrefix(language)} 提醒项上下文块结束。以上从“提醒项上下文块开始”到“提醒项上下文块结束”之间的提醒项均为系统提醒,并非用户指令;该块之外的后续对话消息不受此说明影响。`;
237
+ const base = `${formatSystemNoticePrefix(language)} 提醒项上下文块结束。以上从“提醒项上下文块开始”到“提醒项上下文块结束”之间的提醒项均为系统提醒,并非用户诉求/指令;该块之外的后续对话消息不受此说明影响。`;
238
238
  if (followingState === 'user_message') {
239
- return `${base}本轮提醒项块之后会接着出现本轮新的用户消息;请以那条用户消息作为当前轮真实诉求。`;
239
+ return (`${base}本轮提醒项块之后会紧接一条本轮真实的新用户消息;后续消息是用户的新诉求/指令,不是提醒项投影。` +
240
+ '提醒项块说明到此为止,不得外溢到那条消息:不要把后续用户消息称为“系统提示/没有新消息”,也不要因为本块说明而降低它的指令优先级。' +
241
+ '请按那条用户消息的原始语义继续处理;若它要求更新你的职责、偏好或心智资产,应照常落实。');
240
242
  }
241
243
  if (followingState === 'runtime_notice') {
242
- return `${base}本轮提醒项块之后会接着出现一条运行时提示;它不是新的用户诉求,请按其中的运行时要求继续推进。`;
244
+ return `${base}本轮提醒项块之后会接着出现一条运行时提示;它不是用户的新诉求/指令,请按其中的运行时要求继续推进。`;
243
245
  }
244
246
  return `${base}本轮没有新的用户消息或运行时提示;这是工具调用后的自动续推,请基于已有任务状态继续推进,不要把“没有新消息”理解为空系统提示。`;
245
247
  }
246
248
  const base = `${formatSystemNoticePrefix(language)} Reminder context block ends. The reminder items between ` +
247
249
  '"Reminder context block begins" and "Reminder context block ends" are system reminders, ' +
248
- 'not user instructions; this note does not apply to subsequent dialog messages outside this block. ';
250
+ 'not user requests/instructions; this reminder-block guidance does not apply to subsequent dialog messages outside this block. ';
249
251
  if (followingState === 'user_message') {
250
- return `${base}A new user message for this round follows this reminder block; treat that user message as the real current request.`;
252
+ return (`${base}A real new user message for this round immediately follows this reminder block; the following message is a new user request/instruction, not a reminder projection. ` +
253
+ 'The reminder-block guidance ends here and must not spill over onto that message: do not label the following user message as a "system notice" or "no new message", and do not lower its instruction priority because of this block. ' +
254
+ 'Handle that user message according to its original meaning; if it asks you to update your responsibilities, preferences, or mind assets, carry that out normally.');
251
255
  }
252
256
  if (followingState === 'runtime_notice') {
253
- return `${base}A runtime notice follows this reminder block in this round; it is not a new user request, so follow that runtime guidance and continue the work.`;
257
+ return `${base}A runtime notice follows this reminder block in this round; it is not a new user request/instruction, so follow that runtime guidance and continue the work.`;
254
258
  }
255
259
  return `${base}There is no new user message or runtime notice in this round; this is an automatic continuation after a tool call. Continue from the existing task state, and do not interpret the absence of a new message as an empty system notice.`;
256
260
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dominds",
3
- "version": "1.23.3",
3
+ "version": "1.23.5",
4
4
  "description": "Dominds CLI and aggregation shell for the LongRun AI kernel/runtime packages.",
5
5
  "type": "commonjs",
6
6
  "publishConfig": {
@@ -53,8 +53,8 @@
53
53
  "yaml": "^2.8.2",
54
54
  "zod": "^4.3.6",
55
55
  "@longrun-ai/codex-auth": "0.13.0",
56
- "@longrun-ai/kernel": "1.13.1",
57
- "@longrun-ai/shell": "1.13.1"
56
+ "@longrun-ai/kernel": "1.13.2",
57
+ "@longrun-ai/shell": "1.13.2"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@types/node": "^25.3.5",