dominds 1.27.2 → 1.27.3
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/apps/runtime.js +3 -1
- package/dist/dialog-global-registry.d.ts +11 -1
- package/dist/dialog-global-registry.js +45 -0
- package/dist/dialog.d.ts +11 -2
- package/dist/dialog.js +107 -17
- package/dist/docs/daemon-cmd-runner.md +5 -0
- package/dist/docs/daemon-cmd-runner.zh.md +5 -0
- package/dist/llm/kernel-driver/drive.js +163 -11
- package/dist/llm/kernel-driver/fbr.d.ts +9 -0
- package/dist/llm/kernel-driver/fbr.js +186 -59
- package/dist/minds/load.js +1 -0
- package/dist/persistence.js +18 -1
- package/dist/runtime/driver-messages.d.ts +9 -0
- package/dist/runtime/driver-messages.js +61 -5
- package/dist/runtime/shared-reminder-update-impact.d.ts +20 -0
- package/dist/runtime/shared-reminder-update-impact.js +110 -0
- package/dist/tool-availability.js +1 -0
- package/dist/tools/builtins.js +2 -0
- package/dist/tools/cmd-runner-protocol.d.ts +6 -0
- package/dist/tools/cmd-runner-protocol.js +57 -2
- package/dist/tools/cmd-runner.js +83 -2
- package/dist/tools/ctrl.d.ts +2 -0
- package/dist/tools/ctrl.js +179 -5
- package/dist/tools/os.js +115 -14
- package/dist/tools/process-kill.js +49 -0
- package/dist/tools/prompts/control/en/errors.md +1 -1
- package/dist/tools/prompts/control/en/index.md +1 -1
- package/dist/tools/prompts/control/en/principles.md +18 -17
- package/dist/tools/prompts/control/en/tools.md +24 -1
- package/dist/tools/prompts/control/zh/errors.md +1 -1
- package/dist/tools/prompts/control/zh/index.md +1 -1
- package/dist/tools/prompts/control/zh/principles.md +17 -16
- package/dist/tools/prompts/control/zh/tools.md +24 -1
- package/dist/tools/prompts/os/en/tools.md +2 -0
- package/dist/tools/prompts/os/zh/tools.md +2 -0
- package/package.json +4 -4
package/dist/apps/runtime.js
CHANGED
|
@@ -123,7 +123,9 @@ async function resolveTargetDialog(sourceDlg, target) {
|
|
|
123
123
|
}
|
|
124
124
|
return sideDialog;
|
|
125
125
|
}
|
|
126
|
-
const matches = targetRoot
|
|
126
|
+
const matches = targetRoot
|
|
127
|
+
.getLoadedDialogTreeSnapshot()
|
|
128
|
+
.filter((dialog) => dialog.agentId === target.agentId);
|
|
127
129
|
if (matches.length === 1) {
|
|
128
130
|
return matches[0];
|
|
129
131
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type MainDialog } from './dialog';
|
|
1
|
+
import { type Dialog, type MainDialog } from './dialog';
|
|
2
2
|
export type DriveTriggerEvent = Readonly<{
|
|
3
3
|
type: 'drive_trigger_evt';
|
|
4
4
|
action: 'queue_root_drive' | 'clear_root_drive_queue' | 'active_run_cleared';
|
|
@@ -47,6 +47,16 @@ declare class GlobalDialogRegistry {
|
|
|
47
47
|
noteActiveRunBlockedQueuedDrive(rootId: string): void;
|
|
48
48
|
hasPendingActiveRunClearedDrive(rootId: string): boolean;
|
|
49
49
|
isRootDriveQueued(rootId: string): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* One-shot runtime snapshot for shared-reminder impact routing.
|
|
52
|
+
*
|
|
53
|
+
* This intentionally does not enumerate persisted "all running dialogs". It walks only the roots
|
|
54
|
+
* this runtime has already registered, and includes only dialogs with direct in-memory evidence
|
|
55
|
+
* of current work: locked running main dialogs and the loaded active-callee frontier under each
|
|
56
|
+
* root. Dialogs that merely remain in memory/history but have no current-work signal are not
|
|
57
|
+
* considered parallel work.
|
|
58
|
+
*/
|
|
59
|
+
getLoadedInFlightDialogsForSharedReminderImpact(): Dialog[];
|
|
50
60
|
getLastDriveTrigger(rootId: string): DriveTriggerEvent | undefined;
|
|
51
61
|
consumeQueuedMainDialogs(): MainDialog[];
|
|
52
62
|
get size(): number;
|
|
@@ -218,6 +218,51 @@ class GlobalDialogRegistry {
|
|
|
218
218
|
isRootDriveQueued(rootId) {
|
|
219
219
|
return this.entries.get(rootId)?.driveQueued === true;
|
|
220
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* One-shot runtime snapshot for shared-reminder impact routing.
|
|
223
|
+
*
|
|
224
|
+
* This intentionally does not enumerate persisted "all running dialogs". It walks only the roots
|
|
225
|
+
* this runtime has already registered, and includes only dialogs with direct in-memory evidence
|
|
226
|
+
* of current work: locked running main dialogs and the loaded active-callee frontier under each
|
|
227
|
+
* root. Dialogs that merely remain in memory/history but have no current-work signal are not
|
|
228
|
+
* considered parallel work.
|
|
229
|
+
*/
|
|
230
|
+
getLoadedInFlightDialogsForSharedReminderImpact() {
|
|
231
|
+
const dialogs = [];
|
|
232
|
+
for (const entry of this.entries.values()) {
|
|
233
|
+
const rootDialog = entry.mainDialog;
|
|
234
|
+
const visitedDialogIds = new Set();
|
|
235
|
+
const stack = [];
|
|
236
|
+
if (rootDialog.status === 'running' && rootDialog.isLocked()) {
|
|
237
|
+
dialogs.push(rootDialog);
|
|
238
|
+
visitedDialogIds.add(rootDialog.id.valueOf());
|
|
239
|
+
}
|
|
240
|
+
for (const calleeId of rootDialog.activeCalleeDialogIds) {
|
|
241
|
+
const loadedCallee = rootDialog.lookupDialog(calleeId.selfId);
|
|
242
|
+
if (loadedCallee && loadedCallee.status === 'running') {
|
|
243
|
+
stack.push(loadedCallee);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
while (stack.length > 0) {
|
|
247
|
+
const current = stack.pop();
|
|
248
|
+
if (!current || visitedDialogIds.has(current.id.valueOf())) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
visitedDialogIds.add(current.id.valueOf());
|
|
252
|
+
if (current.status !== 'running') {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
dialogs.push(current);
|
|
256
|
+
for (const calleeId of current.activeCalleeDialogIds) {
|
|
257
|
+
const loadedCallee = rootDialog.lookupDialog(calleeId.selfId);
|
|
258
|
+
if (loadedCallee && loadedCallee.status === 'running') {
|
|
259
|
+
stack.push(loadedCallee);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return dialogs;
|
|
265
|
+
}
|
|
221
266
|
getLastDriveTrigger(rootId) {
|
|
222
267
|
return this.lastDriveTriggerByRootId.get(rootId);
|
|
223
268
|
}
|
package/dist/dialog.d.ts
CHANGED
|
@@ -275,6 +275,8 @@ export declare abstract class Dialog {
|
|
|
275
275
|
private processReminderCollection;
|
|
276
276
|
private buildVisibleReminderContents;
|
|
277
277
|
private emitFullRemindersUpdate;
|
|
278
|
+
private reportReminderRuntimeFailure;
|
|
279
|
+
private dispatchSharedReminderOwnerUpdateImpacts;
|
|
278
280
|
/**
|
|
279
281
|
* Emits current visible reminders without reconciling tool-owned state or writing persistence.
|
|
280
282
|
* This is for read-only dialog display, especially completed/archived dialogs opened from old tabs.
|
|
@@ -371,6 +373,13 @@ export declare abstract class Dialog {
|
|
|
371
373
|
tellaskReplyDirective: TellaskReplyDirective;
|
|
372
374
|
skipTaskdoc?: boolean;
|
|
373
375
|
}): Promise<DialogQueuedPromptState>;
|
|
376
|
+
queueRuntimeGuidePrompt(options: {
|
|
377
|
+
prompt: string;
|
|
378
|
+
msgId: string;
|
|
379
|
+
grammar: 'markdown';
|
|
380
|
+
userLanguageCode?: LanguageCode;
|
|
381
|
+
skipTaskdoc?: boolean;
|
|
382
|
+
}): Promise<DialogQueuedPromptState>;
|
|
374
383
|
queueRuntimeSideDialogPrompt(options: {
|
|
375
384
|
prompt: string;
|
|
376
385
|
msgId: string;
|
|
@@ -529,9 +538,9 @@ export declare class MainDialog extends Dialog {
|
|
|
529
538
|
*/
|
|
530
539
|
lookupDialog(selfId: string): Dialog | undefined;
|
|
531
540
|
/**
|
|
532
|
-
*
|
|
541
|
+
* Snapshot of dialogs loaded in this root's in-memory dialog tree.
|
|
533
542
|
*/
|
|
534
|
-
|
|
543
|
+
getLoadedDialogTreeSnapshot(): Dialog[];
|
|
535
544
|
/**
|
|
536
545
|
* Remove a dialog from the local registry.
|
|
537
546
|
*/
|
package/dist/dialog.js
CHANGED
|
@@ -572,6 +572,7 @@ class Dialog {
|
|
|
572
572
|
}
|
|
573
573
|
async processReminderCollection(reminders) {
|
|
574
574
|
let changed = false;
|
|
575
|
+
const updatedReminderIds = [];
|
|
575
576
|
const indicesToRemove = [];
|
|
576
577
|
for (let i = 0; i < reminders.length; i++) {
|
|
577
578
|
const reminder = reminders[i];
|
|
@@ -599,26 +600,42 @@ class Dialog {
|
|
|
599
600
|
priority: reminder.priority,
|
|
600
601
|
renderMode: reminder.renderMode,
|
|
601
602
|
});
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
if (
|
|
603
|
+
const reminderChanged = (0, tool_1.computeReminderRenderRevision)(updatedReminder) !==
|
|
604
|
+
(0, tool_1.computeReminderRenderRevision)(reminder);
|
|
605
|
+
if (reminderChanged) {
|
|
605
606
|
reminders[i] = updatedReminder;
|
|
606
607
|
changed = true;
|
|
608
|
+
updatedReminderIds.push(updatedReminder.id);
|
|
607
609
|
}
|
|
608
610
|
break;
|
|
609
611
|
}
|
|
610
612
|
case 'keep':
|
|
611
613
|
break;
|
|
614
|
+
default: {
|
|
615
|
+
const _exhaustive = result.treatment;
|
|
616
|
+
return _exhaustive;
|
|
617
|
+
}
|
|
612
618
|
}
|
|
613
619
|
}
|
|
614
620
|
catch (error) {
|
|
615
|
-
|
|
621
|
+
const detail = `Reminder owner update failed ` +
|
|
622
|
+
`(rootId=${this.id.rootId}, selfId=${this.id.selfId}, course=${this.currentCourse}, ` +
|
|
623
|
+
`reminderId=${reminder.id}, owner=${reminder.owner.name}, taskDocPath=${this.taskDocPath})`;
|
|
624
|
+
await this.reportReminderRuntimeFailure(detail, error, {
|
|
625
|
+
rootId: this.id.rootId,
|
|
626
|
+
selfId: this.id.selfId,
|
|
627
|
+
course: this.currentCourse,
|
|
628
|
+
reminderId: reminder.id,
|
|
629
|
+
ownerName: reminder.owner.name,
|
|
630
|
+
taskDocPath: this.taskDocPath,
|
|
631
|
+
});
|
|
632
|
+
throw error;
|
|
616
633
|
}
|
|
617
634
|
}
|
|
618
635
|
for (let i = indicesToRemove.length - 1; i >= 0; i--) {
|
|
619
636
|
reminders.splice(indicesToRemove[i], 1);
|
|
620
637
|
}
|
|
621
|
-
return changed;
|
|
638
|
+
return { changed, updatedReminderIds };
|
|
622
639
|
}
|
|
623
640
|
buildVisibleReminderContents(taskSharedReminders, runtimeReminders) {
|
|
624
641
|
const visibleReminders = [
|
|
@@ -644,6 +661,32 @@ class Dialog {
|
|
|
644
661
|
};
|
|
645
662
|
(0, evt_registry_1.postDialogEvent)(this, fullRemindersEvt);
|
|
646
663
|
}
|
|
664
|
+
async reportReminderRuntimeFailure(detail, error, context) {
|
|
665
|
+
try {
|
|
666
|
+
await this.streamError(detail);
|
|
667
|
+
}
|
|
668
|
+
catch (streamError) {
|
|
669
|
+
log_1.log.warn('Failed to emit stream_error_evt for reminder runtime failure', streamError, {
|
|
670
|
+
...context,
|
|
671
|
+
streamErrorMessage: streamError instanceof Error ? streamError.message : String(streamError),
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
log_1.log.error(detail, error, {
|
|
675
|
+
...context,
|
|
676
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
async dispatchSharedReminderOwnerUpdateImpacts(reminderIds, scope) {
|
|
680
|
+
const { dispatchSharedReminderUpdateImpact } = await import('./runtime/shared-reminder-update-impact.js');
|
|
681
|
+
for (const reminderId of reminderIds) {
|
|
682
|
+
await dispatchSharedReminderUpdateImpact({
|
|
683
|
+
updater: this,
|
|
684
|
+
reminderId,
|
|
685
|
+
scope,
|
|
686
|
+
language: (0, work_language_1.getWorkLanguage)(),
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
}
|
|
647
690
|
/**
|
|
648
691
|
* Emits current visible reminders without reconciling tool-owned state or writing persistence.
|
|
649
692
|
* This is for read-only dialog display, especially completed/archived dialogs opened from old tabs.
|
|
@@ -675,10 +718,10 @@ class Dialog {
|
|
|
675
718
|
const runtimeTarget = { kind: 'agent', agentId: this.agentId };
|
|
676
719
|
const taskSharedReminders = await (0, shared_reminders_1.loadSharedReminders)(taskSharedTarget);
|
|
677
720
|
const runtimeReminders = await (0, shared_reminders_1.loadSharedReminders)(runtimeTarget);
|
|
678
|
-
const
|
|
679
|
-
const
|
|
680
|
-
const
|
|
681
|
-
if (
|
|
721
|
+
const localResult = await this.processReminderCollection(this.reminders);
|
|
722
|
+
const taskSharedResult = await this.processReminderCollection(taskSharedReminders);
|
|
723
|
+
const runtimeResult = await this.processReminderCollection(runtimeReminders);
|
|
724
|
+
if (localResult.changed || taskSharedResult.changed || runtimeResult.changed) {
|
|
682
725
|
this.touchReminders();
|
|
683
726
|
}
|
|
684
727
|
// Centralized persistence - called when emitting event.
|
|
@@ -687,26 +730,55 @@ class Dialog {
|
|
|
687
730
|
await this.dlgStore.persistReminders(this, this.reminders);
|
|
688
731
|
}
|
|
689
732
|
catch (err) {
|
|
690
|
-
|
|
733
|
+
const detail = `Failed to persist dialog-local reminders ` +
|
|
734
|
+
`(rootId=${this.id.rootId}, selfId=${this.id.selfId}, course=${this.currentCourse})`;
|
|
735
|
+
await this.reportReminderRuntimeFailure(detail, err, {
|
|
736
|
+
rootId: this.id.rootId,
|
|
737
|
+
selfId: this.id.selfId,
|
|
738
|
+
course: this.currentCourse,
|
|
739
|
+
taskDocPath: this.taskDocPath,
|
|
740
|
+
});
|
|
741
|
+
throw err;
|
|
691
742
|
}
|
|
692
743
|
try {
|
|
693
744
|
await (0, shared_reminders_1.replaceSharedReminders)(taskSharedTarget, taskSharedReminders);
|
|
694
745
|
}
|
|
695
746
|
catch (err) {
|
|
696
|
-
|
|
697
|
-
|
|
747
|
+
const detail = `Failed to persist task-scoped reminders ` +
|
|
748
|
+
`(rootId=${this.id.rootId}, selfId=${this.id.selfId}, course=${this.currentCourse}, ` +
|
|
749
|
+
`agentId=${this.agentId}, taskDocPath=${this.taskDocPath})`;
|
|
750
|
+
await this.reportReminderRuntimeFailure(detail, err, {
|
|
751
|
+
rootId: this.id.rootId,
|
|
752
|
+
selfId: this.id.selfId,
|
|
753
|
+
course: this.currentCourse,
|
|
698
754
|
agentId: this.agentId,
|
|
699
755
|
taskDocPath: this.taskDocPath,
|
|
700
756
|
});
|
|
757
|
+
throw err;
|
|
701
758
|
}
|
|
759
|
+
await this.dispatchSharedReminderOwnerUpdateImpacts(taskSharedResult.updatedReminderIds, 'task');
|
|
702
760
|
try {
|
|
703
761
|
await (0, shared_reminders_1.replaceSharedReminders)(runtimeTarget, runtimeReminders);
|
|
704
762
|
}
|
|
705
763
|
catch (err) {
|
|
706
|
-
|
|
707
|
-
|
|
764
|
+
const detail = `Failed to persist agent-scoped reminders ` +
|
|
765
|
+
`(rootId=${this.id.rootId}, selfId=${this.id.selfId}, course=${this.currentCourse}, ` +
|
|
766
|
+
`agentId=${this.agentId})`;
|
|
767
|
+
await this.reportReminderRuntimeFailure(detail, err, {
|
|
768
|
+
rootId: this.id.rootId,
|
|
769
|
+
selfId: this.id.selfId,
|
|
770
|
+
course: this.currentCourse,
|
|
708
771
|
agentId: this.agentId,
|
|
772
|
+
taskDocPath: this.taskDocPath,
|
|
709
773
|
});
|
|
774
|
+
throw err;
|
|
775
|
+
}
|
|
776
|
+
for (const reminderId of runtimeResult.updatedReminderIds) {
|
|
777
|
+
const reminder = runtimeReminders.find((candidate) => candidate.id === reminderId);
|
|
778
|
+
if (!reminder) {
|
|
779
|
+
throw new Error(`Updated shared reminder ${reminderId} disappeared before update impact dispatch`);
|
|
780
|
+
}
|
|
781
|
+
await this.dispatchSharedReminderOwnerUpdateImpacts([reminderId], reminder.scope === 'runtime' ? 'runtime' : 'agent');
|
|
710
782
|
}
|
|
711
783
|
const reminders = this.buildVisibleReminderContents(taskSharedReminders, runtimeReminders);
|
|
712
784
|
this.emitFullRemindersUpdate(reminders);
|
|
@@ -1168,6 +1240,24 @@ class Dialog {
|
|
|
1168
1240
|
});
|
|
1169
1241
|
return created;
|
|
1170
1242
|
}
|
|
1243
|
+
async queueRuntimeGuidePrompt(options) {
|
|
1244
|
+
const common = this.runtimePromptCommon(options);
|
|
1245
|
+
const created = {
|
|
1246
|
+
...common,
|
|
1247
|
+
kind: 'new_course_runtime_guide',
|
|
1248
|
+
skipTaskdoc: options.skipTaskdoc,
|
|
1249
|
+
};
|
|
1250
|
+
this.enqueueQueuedPromptState(created);
|
|
1251
|
+
await this.persistPendingRuntimePrompt({
|
|
1252
|
+
content: created.prompt,
|
|
1253
|
+
msgId: created.msgId,
|
|
1254
|
+
grammar: created.grammar ?? 'markdown',
|
|
1255
|
+
userLanguageCode: created.userLanguageCode,
|
|
1256
|
+
origin: 'runtime',
|
|
1257
|
+
skipTaskdoc: created.skipTaskdoc,
|
|
1258
|
+
});
|
|
1259
|
+
return created;
|
|
1260
|
+
}
|
|
1171
1261
|
async queueRuntimeSideDialogPrompt(options) {
|
|
1172
1262
|
const common = this.runtimePromptCommon(options);
|
|
1173
1263
|
const created = {
|
|
@@ -1860,7 +1950,7 @@ exports.SideDialog = SideDialog;
|
|
|
1860
1950
|
*/
|
|
1861
1951
|
class MainDialog extends Dialog {
|
|
1862
1952
|
_status = 'running';
|
|
1863
|
-
// Tracks
|
|
1953
|
+
// Tracks dialogs loaded into this root's in-memory dialog tree for O(1) lookup
|
|
1864
1954
|
_localRegistry = new Map();
|
|
1865
1955
|
// Tracks Type-B registered sideDialogs by agentId!sessionSlug
|
|
1866
1956
|
_sideDialogRegistry = new Map();
|
|
@@ -1887,9 +1977,9 @@ class MainDialog extends Dialog {
|
|
|
1887
1977
|
return this._localRegistry.get(selfId);
|
|
1888
1978
|
}
|
|
1889
1979
|
/**
|
|
1890
|
-
*
|
|
1980
|
+
* Snapshot of dialogs loaded in this root's in-memory dialog tree.
|
|
1891
1981
|
*/
|
|
1892
|
-
|
|
1982
|
+
getLoadedDialogTreeSnapshot() {
|
|
1893
1983
|
return Array.from(this._localRegistry.values());
|
|
1894
1984
|
}
|
|
1895
1985
|
/**
|
|
@@ -115,6 +115,8 @@ Ownership boundaries are simple:
|
|
|
115
115
|
- `pid: number`
|
|
116
116
|
- `stdout?: boolean`
|
|
117
117
|
- `stderr?: boolean`
|
|
118
|
+
- `wait_for_new_output?: boolean`
|
|
119
|
+
- `timeout_ms?: number`
|
|
118
120
|
|
|
119
121
|
### Semantics
|
|
120
122
|
|
|
@@ -123,6 +125,9 @@ Ownership boundaries are simple:
|
|
|
123
125
|
- output order is always `stdout` first, then `stderr`
|
|
124
126
|
- each stream gets its own heading, content block, and scroll notice
|
|
125
127
|
- unrequested streams are omitted entirely
|
|
128
|
+
- when `wait_for_new_output=true`, the runner waits until at least one requested stream receives output after the request is accepted
|
|
129
|
+
- if `timeout_ms` is provided, the wait is bounded by that many milliseconds, capped at `86400000` (24h), and returns the current snapshot with an explicit timeout notice
|
|
130
|
+
- `timeout_ms` cannot be combined with `wait_for_new_output=false`; that contradictory argument set returns an error
|
|
126
131
|
|
|
127
132
|
### Why this is better
|
|
128
133
|
|
|
@@ -114,6 +114,8 @@ runner 自己作为进程组 leader,daemon 默认继承该 pgid。这样 `stop
|
|
|
114
114
|
- `pid: number`
|
|
115
115
|
- `stdout?: boolean`
|
|
116
116
|
- `stderr?: boolean`
|
|
117
|
+
- `wait_for_new_output?: boolean`
|
|
118
|
+
- `timeout_ms?: number`
|
|
117
119
|
|
|
118
120
|
### 语义
|
|
119
121
|
|
|
@@ -122,6 +124,9 @@ runner 自己作为进程组 leader,daemon 默认继承该 pgid。这样 `stop
|
|
|
122
124
|
- 返回顺序固定为 `stdout` 在前、`stderr` 在后
|
|
123
125
|
- 每个流各自带自己的标题、内容、scroll notice
|
|
124
126
|
- 未请求的流不展示
|
|
127
|
+
- `wait_for_new_output=true` 时,runner 会等到请求的流中至少一个在请求被接受后出现新输出再返回
|
|
128
|
+
- 提供 `timeout_ms` 时,等待最多持续指定毫秒数,上限为 `86400000`(24 小时);超时后返回当前快照,并显式提示等待超时
|
|
129
|
+
- `timeout_ms` 不可与 `wait_for_new_output=false` 同时使用;这种矛盾参数会直接报错
|
|
125
130
|
|
|
126
131
|
### 取舍理由
|
|
127
132
|
|
|
@@ -46,6 +46,7 @@ const KERNEL_DRIVER_DEFAULT_RETRY_POLICY = {
|
|
|
46
46
|
backoffMultiplier: 1.5,
|
|
47
47
|
maxDelayMs: 30 * 60 * 1000, // 30 minutes
|
|
48
48
|
};
|
|
49
|
+
const CLEAR_MIND_TOOL_NAME = 'clear_mind';
|
|
49
50
|
const KERNEL_DRIVER_EMPTY_LLM_RESPONSE_ERROR_CODE = 'DOMINDS_LLM_EMPTY_RESPONSE';
|
|
50
51
|
// Wrapper isolation boundary:
|
|
51
52
|
// - Wrappers emit provider-native web-search events.
|
|
@@ -246,6 +247,35 @@ function buildKernelDriverFbrPrompt(dlg, state) {
|
|
|
246
247
|
origin: 'runtime',
|
|
247
248
|
};
|
|
248
249
|
}
|
|
250
|
+
function resolveLatestModelOutputGenseq(dlg) {
|
|
251
|
+
for (let index = dlg.msgs.length - 1; index >= 0; index -= 1) {
|
|
252
|
+
const msg = dlg.msgs[index];
|
|
253
|
+
if (msg === undefined) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
switch (msg.type) {
|
|
257
|
+
case 'saying_msg':
|
|
258
|
+
case 'thinking_msg':
|
|
259
|
+
case 'func_call_msg': {
|
|
260
|
+
const genseq = Math.floor(msg.genseq);
|
|
261
|
+
if (Number.isFinite(genseq) && genseq > 0) {
|
|
262
|
+
return genseq;
|
|
263
|
+
}
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
default:
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
function resolveProgrammaticFbrConclusionGenseq(args) {
|
|
273
|
+
return (args.lastAssistantSayingGenseq ??
|
|
274
|
+
args.lastFunctionCallGenseq ??
|
|
275
|
+
resolveLatestModelOutputGenseq(args.dlg) ??
|
|
276
|
+
args.dlg.activeGenSeqOrUndefined ??
|
|
277
|
+
1);
|
|
278
|
+
}
|
|
249
279
|
function normalizeQ4HAnswerCallId(raw) {
|
|
250
280
|
if (typeof raw !== 'string')
|
|
251
281
|
return undefined;
|
|
@@ -750,7 +780,7 @@ function formatContextHealthLargeToolReturnUnavailable(args) {
|
|
|
750
780
|
'1. 把需要回传给主线对话的结论、证据定位和风险整理清楚。',
|
|
751
781
|
'2. 对于下一程恢复工作需要的信息,写入提醒项。',
|
|
752
782
|
'',
|
|
753
|
-
'
|
|
783
|
+
'然后调用 clear_mind({}) 开启新一程,并尽快完成当前支线回复。',
|
|
754
784
|
'',
|
|
755
785
|
`详情:本次返回约 ${approxBytes} 字节。`,
|
|
756
786
|
].join('\n');
|
|
@@ -775,7 +805,7 @@ function formatContextHealthLargeToolReturnUnavailable(args) {
|
|
|
775
805
|
'1. Organize the conclusions, evidence pointers, and risks that need to go back to the Mainline dialog.',
|
|
776
806
|
'2. Write any details needed to resume the next course into reminders.',
|
|
777
807
|
'',
|
|
778
|
-
'Then finish the current Sideline dialog reply as soon as possible
|
|
808
|
+
'Then call clear_mind({}) to start a new course, and finish the current Sideline dialog reply as soon as possible.',
|
|
779
809
|
'',
|
|
780
810
|
`Detail: this return was about ${approxBytes} bytes.`,
|
|
781
811
|
].join('\n');
|
|
@@ -814,6 +844,9 @@ function countToolResultVisibleBytes(output) {
|
|
|
814
844
|
return bytes;
|
|
815
845
|
}
|
|
816
846
|
function applyContextHealthToolResultVisibilityLimit(args) {
|
|
847
|
+
if (isFbrSideDialog(args.dlg)) {
|
|
848
|
+
return { output: args.output, largeReturnUnavailable: false };
|
|
849
|
+
}
|
|
817
850
|
if ((0, context_health_1.getContextHealthRemediationLevel)(args.contextHealth) === undefined) {
|
|
818
851
|
return { output: args.output, largeReturnUnavailable: false };
|
|
819
852
|
}
|
|
@@ -1961,6 +1994,30 @@ function shouldImmediatelyFollowUpToolOutcome(tool, outcome) {
|
|
|
1961
1994
|
}
|
|
1962
1995
|
return shouldImmediatelyFollowUpSuccessfulToolResult(tool);
|
|
1963
1996
|
}
|
|
1997
|
+
function isFailedToolOutcome(outcome) {
|
|
1998
|
+
return outcome === 'failure' || outcome === 'partial_failure';
|
|
1999
|
+
}
|
|
2000
|
+
function formatClearMindBlockedByFailedSiblingTools(failedTools) {
|
|
2001
|
+
const language = (0, work_language_1.getWorkLanguage)();
|
|
2002
|
+
const details = failedTools
|
|
2003
|
+
.map((tool) => `- ${tool.toolName} (callId=${tool.callId}, outcome=${String(tool.outcome)})`)
|
|
2004
|
+
.join('\n');
|
|
2005
|
+
return language === 'zh'
|
|
2006
|
+
? [
|
|
2007
|
+
'错误:本轮 clear_mind 与其它工具一起调用,但其它工具返回了失败结果。',
|
|
2008
|
+
'',
|
|
2009
|
+
details,
|
|
2010
|
+
'',
|
|
2011
|
+
'clear_mind 已拒绝开启新一程。请先确保其它工具调用正常完成(必要时修正参数、重试或处理失败),然后再次调用 clear_mind。',
|
|
2012
|
+
].join('\n')
|
|
2013
|
+
: [
|
|
2014
|
+
'Error: clear_mind was called in the same round as other tools, and at least one other tool returned a failure result.',
|
|
2015
|
+
'',
|
|
2016
|
+
details,
|
|
2017
|
+
'',
|
|
2018
|
+
'clear_mind refused to start a new course. Ensure the other tool calls complete normally first (fix arguments, retry, or handle the failure), then call clear_mind again.',
|
|
2019
|
+
].join('\n');
|
|
2020
|
+
}
|
|
1964
2021
|
function trimOptionalCallId(value) {
|
|
1965
2022
|
if (typeof value !== 'string')
|
|
1966
2023
|
return undefined;
|
|
@@ -2133,7 +2190,7 @@ async function executeFunctionCalls(args) {
|
|
|
2133
2190
|
throwIfAborted(args.abortSignal, args.dlg);
|
|
2134
2191
|
await args.dlg.persistFunctionCall(prepared.func.id, prepared.func.name, prepared.argsStr, prepared.callGenseq, prepared.func.rawId);
|
|
2135
2192
|
}
|
|
2136
|
-
const
|
|
2193
|
+
const executePreparedCall = async ({ func, originalFunc, callGenseq, argsStr, tool, preparedInvocationArgs, }) => {
|
|
2137
2194
|
let result;
|
|
2138
2195
|
let outcome = 'success';
|
|
2139
2196
|
let forceImmediateFollowup = false;
|
|
@@ -2226,8 +2283,54 @@ async function executeFunctionCalls(args) {
|
|
|
2226
2283
|
throw rethrowError;
|
|
2227
2284
|
}
|
|
2228
2285
|
return { func, originalFunc, outcome, forceImmediateFollowup, result };
|
|
2229
|
-
}
|
|
2230
|
-
|
|
2286
|
+
};
|
|
2287
|
+
const blockClearMindCall = async (prepared, failedTools) => {
|
|
2288
|
+
const output = (0, tool_1.toolFailure)(formatClearMindBlockedByFailedSiblingTools(failedTools));
|
|
2289
|
+
const result = {
|
|
2290
|
+
type: 'func_result_msg',
|
|
2291
|
+
id: prepared.func.id,
|
|
2292
|
+
rawId: prepared.func.rawId,
|
|
2293
|
+
effectiveId: prepared.func.effectiveId,
|
|
2294
|
+
name: prepared.func.name,
|
|
2295
|
+
content: output.content,
|
|
2296
|
+
role: 'tool',
|
|
2297
|
+
genseq: prepared.callGenseq,
|
|
2298
|
+
};
|
|
2299
|
+
await args.dlg.receiveFuncResult(result);
|
|
2300
|
+
return {
|
|
2301
|
+
func: prepared.func,
|
|
2302
|
+
originalFunc: prepared.originalFunc,
|
|
2303
|
+
outcome: output.outcome,
|
|
2304
|
+
forceImmediateFollowup: false,
|
|
2305
|
+
result,
|
|
2306
|
+
};
|
|
2307
|
+
};
|
|
2308
|
+
const clearMindCalls = preparedCalls.filter((call) => call.func.name === CLEAR_MIND_TOOL_NAME);
|
|
2309
|
+
const siblingCalls = preparedCalls.filter((call) => call.func.name !== CLEAR_MIND_TOOL_NAME);
|
|
2310
|
+
const failedPriorToolResults = args.failedPriorToolResults ?? [];
|
|
2311
|
+
if (clearMindCalls.length === 0 || siblingCalls.length === 0) {
|
|
2312
|
+
if (clearMindCalls.length > 0 && failedPriorToolResults.length > 0) {
|
|
2313
|
+
return await Promise.all(clearMindCalls.map((call) => blockClearMindCall(call, failedPriorToolResults)));
|
|
2314
|
+
}
|
|
2315
|
+
return await Promise.all(preparedCalls.map((call) => executePreparedCall(call)));
|
|
2316
|
+
}
|
|
2317
|
+
const siblingExecutions = await Promise.all(siblingCalls.map((call) => executePreparedCall(call)));
|
|
2318
|
+
const failedSiblingToolResults = [
|
|
2319
|
+
...failedPriorToolResults,
|
|
2320
|
+
...siblingExecutions
|
|
2321
|
+
.filter((execution) => isFailedToolOutcome(execution.outcome))
|
|
2322
|
+
.map((execution) => ({
|
|
2323
|
+
callId: execution.result.id,
|
|
2324
|
+
toolName: execution.result.name,
|
|
2325
|
+
outcome: execution.outcome,
|
|
2326
|
+
})),
|
|
2327
|
+
];
|
|
2328
|
+
if (failedSiblingToolResults.length > 0) {
|
|
2329
|
+
const clearMindExecutions = await Promise.all(clearMindCalls.map((call) => blockClearMindCall(call, failedSiblingToolResults)));
|
|
2330
|
+
return [...siblingExecutions, ...clearMindExecutions];
|
|
2331
|
+
}
|
|
2332
|
+
const clearMindExecutions = await Promise.all(clearMindCalls.map((call) => executePreparedCall(call)));
|
|
2333
|
+
return [...siblingExecutions, ...clearMindExecutions];
|
|
2231
2334
|
}
|
|
2232
2335
|
async function executeFunctionRound(args) {
|
|
2233
2336
|
if (args.funcCalls.length === 0) {
|
|
@@ -2270,6 +2373,17 @@ async function executeFunctionRound(args) {
|
|
|
2270
2373
|
activePromptReplyDirective: args.activePromptReplyDirective,
|
|
2271
2374
|
});
|
|
2272
2375
|
throwIfAborted(args.abortSignal, args.dlg);
|
|
2376
|
+
const failedTellaskToolResults = tellaskRound.invalidTellaskCallIds.map((callId) => {
|
|
2377
|
+
const call = args.funcCalls.find((candidate) => candidate.id === callId);
|
|
2378
|
+
if (call === undefined) {
|
|
2379
|
+
throw new Error(`kernel-driver tellask result invariant violation: missing invalid tellask call '${callId}'`);
|
|
2380
|
+
}
|
|
2381
|
+
return {
|
|
2382
|
+
callId,
|
|
2383
|
+
toolName: call.name,
|
|
2384
|
+
outcome: 'failure',
|
|
2385
|
+
};
|
|
2386
|
+
});
|
|
2273
2387
|
const genericExecutions = await executeFunctionCalls({
|
|
2274
2388
|
dlg: args.dlg,
|
|
2275
2389
|
agent: args.agent,
|
|
@@ -2277,6 +2391,7 @@ async function executeFunctionRound(args) {
|
|
|
2277
2391
|
funcCalls: tellaskRound.normalCalls,
|
|
2278
2392
|
abortSignal: args.abortSignal,
|
|
2279
2393
|
contextHealthForToolResultVisibility: args.contextHealthForToolResultVisibility,
|
|
2394
|
+
failedPriorToolResults: failedTellaskToolResults,
|
|
2280
2395
|
});
|
|
2281
2396
|
const genericExecutionByOriginalCall = new Map(genericExecutions.map((execution) => [execution.originalFunc, execution]));
|
|
2282
2397
|
const funcToolByName = new Map(args.agentTools
|
|
@@ -2602,8 +2717,41 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
|
|
|
2602
2717
|
for (;;) {
|
|
2603
2718
|
genIterNo += 1;
|
|
2604
2719
|
throwIfAborted(abortSignal, dlg);
|
|
2605
|
-
|
|
2720
|
+
let activeFbrState = await loadDialogFbrState(dlg);
|
|
2606
2721
|
if (isFbrSideDialog(dlg)) {
|
|
2722
|
+
const fbrContextHealthLevel = (0, context_health_1.getContextHealthRemediationLevel)(dlg.getLastContextHealth());
|
|
2723
|
+
if (activeFbrState !== undefined && fbrContextHealthLevel === 'critical') {
|
|
2724
|
+
log_1.log.warn('kernel-driver ending FBR with critical context fixed conclusion', undefined, {
|
|
2725
|
+
rootId: dlg.id.rootId,
|
|
2726
|
+
selfId: dlg.id.selfId,
|
|
2727
|
+
course: dlg.currentCourse,
|
|
2728
|
+
phase: activeFbrState.phase,
|
|
2729
|
+
iteration: activeFbrState.iteration,
|
|
2730
|
+
});
|
|
2731
|
+
fbrConclusion = {
|
|
2732
|
+
responseText: (0, fbr_1.buildProgrammaticFbrContextCriticalContent)({
|
|
2733
|
+
language: (0, work_language_1.getWorkLanguage)(),
|
|
2734
|
+
}),
|
|
2735
|
+
responseGenseq: resolveProgrammaticFbrConclusionGenseq({
|
|
2736
|
+
dlg,
|
|
2737
|
+
lastAssistantSayingGenseq,
|
|
2738
|
+
lastFunctionCallGenseq,
|
|
2739
|
+
}),
|
|
2740
|
+
replyResolutionCallId: `fbr-context-critical-${(0, id_1.generateShortId)()}`,
|
|
2741
|
+
};
|
|
2742
|
+
await persistDialogFbrState(dlg, undefined);
|
|
2743
|
+
dlg.setFbrConclusionToolsEnabled(false);
|
|
2744
|
+
finalDisplayState = await (0, dialog_display_state_1.computeIdleDisplayState)(dlg);
|
|
2745
|
+
break driveCoreLoop;
|
|
2746
|
+
}
|
|
2747
|
+
if (activeFbrState !== undefined &&
|
|
2748
|
+
fbrContextHealthLevel === 'caution' &&
|
|
2749
|
+
!(0, fbr_1.isFbrContextCautionFinalizationState)(activeFbrState) &&
|
|
2750
|
+
(pendingPrompt === undefined || pendingPrompt.origin === 'runtime')) {
|
|
2751
|
+
activeFbrState = (0, fbr_1.forceFbrContextCautionFinalizationState)(activeFbrState);
|
|
2752
|
+
await persistDialogFbrState(dlg, activeFbrState);
|
|
2753
|
+
pendingPrompt = buildKernelDriverFbrPrompt(dlg, activeFbrState);
|
|
2754
|
+
}
|
|
2607
2755
|
dlg.setFbrConclusionToolsEnabled(activeFbrState !== undefined && (0, fbr_1.isFbrFinalizationState)(activeFbrState));
|
|
2608
2756
|
if (pendingPrompt === undefined &&
|
|
2609
2757
|
activeFbrState &&
|
|
@@ -2786,7 +2934,10 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
|
|
|
2786
2934
|
let currentRuntimeGuideMsg;
|
|
2787
2935
|
const currentPromptFromFbrState = currentPrompt !== undefined &&
|
|
2788
2936
|
currentFbrState !== undefined &&
|
|
2789
|
-
currentFbrState.promptDelivered !== true
|
|
2937
|
+
currentFbrState.promptDelivered !== true &&
|
|
2938
|
+
isFbrSideDialog(dlg) &&
|
|
2939
|
+
currentPrompt.origin === 'runtime' &&
|
|
2940
|
+
currentPrompt.content === buildKernelDriverFbrPrompt(dlg, currentFbrState).content;
|
|
2790
2941
|
pendingPrompt = undefined;
|
|
2791
2942
|
if (currentPrompt) {
|
|
2792
2943
|
const diligenceSkipReason = await shouldSkipDiligencePromptBeforeGeneration({
|
|
@@ -3780,10 +3931,11 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
|
|
|
3780
3931
|
language: (0, work_language_1.getWorkLanguage)(),
|
|
3781
3932
|
finalizationAttempts: persistedFbrState.effort,
|
|
3782
3933
|
}),
|
|
3783
|
-
responseGenseq:
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3934
|
+
responseGenseq: resolveProgrammaticFbrConclusionGenseq({
|
|
3935
|
+
dlg,
|
|
3936
|
+
lastAssistantSayingGenseq,
|
|
3937
|
+
lastFunctionCallGenseq,
|
|
3938
|
+
}),
|
|
3787
3939
|
replyResolutionCallId: `fbr-conclusion-${(0, id_1.generateShortId)()}`,
|
|
3788
3940
|
};
|
|
3789
3941
|
if (!isFbrSideDialog(dlg)) {
|
|
@@ -32,6 +32,13 @@ export declare function buildFbrFinalizationPrompt(args: {
|
|
|
32
32
|
total: number;
|
|
33
33
|
language: LanguageCode;
|
|
34
34
|
}): string;
|
|
35
|
+
export declare function buildFbrContextCautionFinalizationPrompt(args: {
|
|
36
|
+
attempt: number;
|
|
37
|
+
language: LanguageCode;
|
|
38
|
+
}): string;
|
|
39
|
+
export declare function buildProgrammaticFbrContextCriticalContent(args: {
|
|
40
|
+
language: LanguageCode;
|
|
41
|
+
}): string;
|
|
35
42
|
export declare function buildProgrammaticFbrUnreasonableSituationContent(args: {
|
|
36
43
|
language: LanguageCode;
|
|
37
44
|
finalizationAttempts: number;
|
|
@@ -40,6 +47,8 @@ export declare function inspectFbrConclusionAttempt(messages: readonly ChatMessa
|
|
|
40
47
|
export declare function createInitialFbrState(effort: number): DialogFbrState;
|
|
41
48
|
export declare function markFbrPromptDelivered(state: DialogFbrState): DialogFbrState;
|
|
42
49
|
export declare function isFbrFinalizationState(state: DialogFbrState): boolean;
|
|
50
|
+
export declare function isFbrContextCautionFinalizationState(state: DialogFbrState): boolean;
|
|
51
|
+
export declare function forceFbrContextCautionFinalizationState(state: DialogFbrState): DialogFbrState;
|
|
43
52
|
export declare function advanceFbrState(state: DialogFbrState): DialogFbrState | undefined;
|
|
44
53
|
export declare function buildFbrPromptForState(args: {
|
|
45
54
|
state: DialogFbrState;
|