dominds 1.27.2 → 1.27.4
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-fork.js +2 -1
- package/dist/dialog-global-registry.d.ts +11 -1
- package/dist/dialog-global-registry.js +45 -0
- package/dist/dialog.d.ts +14 -5
- package/dist/dialog.js +114 -21
- 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 +228 -49
- package/dist/llm/kernel-driver/fbr.d.ts +9 -0
- package/dist/llm/kernel-driver/fbr.js +186 -59
- package/dist/mcp/supervisor.js +4 -1
- package/dist/minds/load.js +1 -0
- package/dist/minds/system-prompt-parts.js +30 -30
- package/dist/persistence.js +83 -17
- package/dist/priming.js +2 -3
- package/dist/runtime/driver-messages.d.ts +9 -0
- package/dist/runtime/driver-messages.js +103 -33
- package/dist/runtime/shared-reminder-update-impact.d.ts +20 -0
- package/dist/runtime/shared-reminder-update-impact.js +110 -0
- package/dist/shared-reminders.js +2 -2
- package/dist/tool-availability.js +1 -0
- package/dist/tool.d.ts +7 -4
- package/dist/tool.js +10 -4
- package/dist/tools/app-reminders.js +4 -3
- 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 +183 -6
- package/dist/tools/os.js +115 -14
- package/dist/tools/pending-tellask-reminder.js +1 -0
- 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 +4 -4
- package/dist/tools/prompts/control/en/principles.md +22 -21
- package/dist/tools/prompts/control/en/scenarios.md +10 -3
- package/dist/tools/prompts/control/en/tools.md +28 -5
- package/dist/tools/prompts/control/zh/errors.md +1 -1
- package/dist/tools/prompts/control/zh/index.md +4 -4
- package/dist/tools/prompts/control/zh/principles.md +21 -20
- package/dist/tools/prompts/control/zh/scenarios.md +10 -3
- package/dist/tools/prompts/control/zh/tools.md +28 -5
- 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/tools/ctrl.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* - add_reminder: Add a reminder
|
|
14
14
|
* - delete_reminder: Delete a reminder by id
|
|
15
15
|
* - update_reminder: Update reminder content
|
|
16
|
+
* - migrate_reminder: Move a visible shared reminder back into the current dialog
|
|
16
17
|
* - clear_mind: Start a new course, optionally add a reminder
|
|
17
18
|
* - do_mind: Main Dialog only; create a new `.tsk/` Taskdoc section without starting a new course
|
|
18
19
|
* - change_mind: Main Dialog only; update a `.tsk/` Taskdoc section without starting a new course
|
|
@@ -63,12 +64,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
63
64
|
};
|
|
64
65
|
})();
|
|
65
66
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
66
|
-
exports.recallTaskdocTool = exports.mindMoreTool = exports.neverMindTool = exports.doMindTool = exports.changeMindTool = exports.clearMindTool = exports.updateReminderTool = exports.addReminderTool = exports.deleteReminderTool = void 0;
|
|
67
|
+
exports.recallTaskdocTool = exports.mindMoreTool = exports.neverMindTool = exports.doMindTool = exports.changeMindTool = exports.clearMindTool = exports.migrateReminderTool = exports.updateReminderTool = exports.addReminderTool = exports.deleteReminderTool = void 0;
|
|
67
68
|
const fs = __importStar(require("fs"));
|
|
68
69
|
const path = __importStar(require("path"));
|
|
69
70
|
const dialog_1 = require("../dialog");
|
|
70
71
|
const rtws_1 = require("../rtws");
|
|
71
72
|
const driver_messages_1 = require("../runtime/driver-messages");
|
|
73
|
+
const shared_reminder_update_impact_1 = require("../runtime/shared-reminder-update-impact");
|
|
72
74
|
const tool_result_messages_1 = require("../runtime/tool-result-messages");
|
|
73
75
|
const work_language_1 = require("../runtime/work-language");
|
|
74
76
|
const shared_reminders_1 = require("../shared-reminders");
|
|
@@ -276,6 +278,87 @@ function replaceReminderContent(reminder, content, meta, renderMode) {
|
|
|
276
278
|
renderMode: renderMode ?? reminder.renderMode,
|
|
277
279
|
});
|
|
278
280
|
}
|
|
281
|
+
function resolveSharedReminderUpdateImpactScope(target) {
|
|
282
|
+
if (target.source !== 'runtime') {
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
if (target.target.kind === 'task') {
|
|
286
|
+
return 'task';
|
|
287
|
+
}
|
|
288
|
+
return target.reminder.scope === 'runtime' ? 'runtime' : 'agent';
|
|
289
|
+
}
|
|
290
|
+
function appendSharedReminderUpdateImpactToToolResult(output, language, reminderId, dispatch) {
|
|
291
|
+
if (dispatch === undefined) {
|
|
292
|
+
return output;
|
|
293
|
+
}
|
|
294
|
+
const notice = (0, driver_messages_1.formatSharedReminderUpdateImpactNotice)(language, {
|
|
295
|
+
reminderId,
|
|
296
|
+
scope: dispatch.scope,
|
|
297
|
+
audience: 'updater',
|
|
298
|
+
});
|
|
299
|
+
const dispatchLine = language === 'zh'
|
|
300
|
+
? `已向 ${dispatch.dispatchedDialogCount}/${dispatch.peerDialogCount} 个受影响的并行对话派发提醒。`
|
|
301
|
+
: `Dispatched notices to ${dispatch.dispatchedDialogCount}/${dispatch.peerDialogCount} affected parallel dialog(s).`;
|
|
302
|
+
return {
|
|
303
|
+
...output,
|
|
304
|
+
content: `${output.content}\n\n${notice}\n${dispatchLine}`,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function appendSharedReminderMigrationImpactToToolResult(output, language, dispatch) {
|
|
308
|
+
if (dispatch === undefined) {
|
|
309
|
+
return output;
|
|
310
|
+
}
|
|
311
|
+
const dispatchLine = language === 'zh'
|
|
312
|
+
? `已向 ${dispatch.dispatchedDialogCount}/${dispatch.peerDialogCount} 个受影响的并行对话派发撤下提醒。`
|
|
313
|
+
: `Dispatched withdrawal notices to ${dispatch.dispatchedDialogCount}/${dispatch.peerDialogCount} affected parallel dialog(s).`;
|
|
314
|
+
return {
|
|
315
|
+
...output,
|
|
316
|
+
content: `${output.content}\n${dispatchLine}`,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
async function formatUpdateReminderSuccessResult(args) {
|
|
320
|
+
const scope = resolveSharedReminderUpdateImpactScope(args.target);
|
|
321
|
+
const dispatch = scope === undefined
|
|
322
|
+
? undefined
|
|
323
|
+
: await (0, shared_reminder_update_impact_1.dispatchSharedReminderUpdateImpact)({
|
|
324
|
+
updater: args.dlg,
|
|
325
|
+
reminderId: args.target.reminder.id,
|
|
326
|
+
scope,
|
|
327
|
+
language: args.language,
|
|
328
|
+
});
|
|
329
|
+
return appendSharedReminderUpdateImpactToToolResult((0, tool_result_messages_1.formatToolActionResult)(args.language, 'updated'), args.language, args.target.reminder.id, dispatch);
|
|
330
|
+
}
|
|
331
|
+
async function migrateSharedReminderTargetToDialog(args) {
|
|
332
|
+
const reminderId = args.target.reminder.id;
|
|
333
|
+
const existingDialogIndex = findReminderIndexById('dialog', args.dlg.reminders, reminderId);
|
|
334
|
+
if (existingDialogIndex !== null) {
|
|
335
|
+
throw new Error(`Duplicate visible reminder_id before migration: ${reminderId}`);
|
|
336
|
+
}
|
|
337
|
+
let migratedReminder;
|
|
338
|
+
await (0, shared_reminders_1.mutateSharedReminders)(args.target.target, (sharedReminders) => {
|
|
339
|
+
const index = findReminderIndexById('shared', sharedReminders, reminderId);
|
|
340
|
+
if (index === null)
|
|
341
|
+
return;
|
|
342
|
+
migratedReminder = sharedReminders[index];
|
|
343
|
+
sharedReminders.splice(index, 1);
|
|
344
|
+
});
|
|
345
|
+
if (migratedReminder === undefined) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
args.dlg.reminders.push((0, tool_1.materializeReminder)({
|
|
349
|
+
id: migratedReminder.id,
|
|
350
|
+
content: migratedReminder.content,
|
|
351
|
+
owner: migratedReminder.owner,
|
|
352
|
+
meta: migratedReminder.meta,
|
|
353
|
+
echoback: migratedReminder.echoback,
|
|
354
|
+
scope: 'dialog',
|
|
355
|
+
createdAt: migratedReminder.createdAt,
|
|
356
|
+
priority: migratedReminder.priority,
|
|
357
|
+
renderMode: migratedReminder.renderMode,
|
|
358
|
+
}));
|
|
359
|
+
args.dlg.touchReminders();
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
279
362
|
async function deleteResolvedReminderTarget(dlg, target) {
|
|
280
363
|
switch (target.source) {
|
|
281
364
|
case 'dialog': {
|
|
@@ -325,6 +408,9 @@ function getCtrlMessages(language) {
|
|
|
325
408
|
invalidFormatAdd: '参数格式不对。用法:add_reminder({ content: string, scope?: "dialog" | "task" | "agent" })(省略 scope 表示 task)',
|
|
326
409
|
reminderContentEmpty: '提醒内容不能为空',
|
|
327
410
|
invalidFormatUpdate: '参数格式不对。用法:update_reminder({ reminder_id: string, content: string })',
|
|
411
|
+
invalidFormatMigrate: '参数格式不对。用法:migrate_reminder({ reminder_id: string, scope: "dialog" })',
|
|
412
|
+
reminderAlreadyDialogScope: (reminderId) => `reminder_id=${reminderId} 已经是当前对话范围提醒项,不需要迁移。`,
|
|
413
|
+
reminderMigrateManagedBlocked: (managerTool) => `错误:该提醒项由工具 ${managerTool} 管理,不能用 migrate_reminder 迁移;请使用 ${managerTool} 更新。`,
|
|
328
414
|
invalidFormatDoMind: '参数格式不对。用法:do_mind({ selector: string, category?: string, content: string })',
|
|
329
415
|
invalidFormatChangeMind: '参数格式不对。用法:change_mind({ selector: string, category?: string, content: string, previous_content_hash: string })',
|
|
330
416
|
tooManyArgsChangeMind: '参数格式不对。用法:change_mind({ selector: string, category?: string, content: string, previous_content_hash: string })',
|
|
@@ -372,6 +458,9 @@ function getCtrlMessages(language) {
|
|
|
372
458
|
invalidFormatAdd: 'Error: Invalid args. Use: add_reminder({ content: string, scope?: "dialog" | "task" | "agent" }) (omitting scope means task).',
|
|
373
459
|
reminderContentEmpty: 'Error: Reminder content cannot be empty',
|
|
374
460
|
invalidFormatUpdate: 'Error: Invalid args. Use: update_reminder({ reminder_id: string, content: string })',
|
|
461
|
+
invalidFormatMigrate: 'Error: Invalid args. Use: migrate_reminder({ reminder_id: string, scope: "dialog" })',
|
|
462
|
+
reminderAlreadyDialogScope: (reminderId) => `reminder_id=${reminderId} is already dialog-scope in the current dialog; no migration is needed.`,
|
|
463
|
+
reminderMigrateManagedBlocked: (managerTool) => `Error: This reminder is managed by tool ${managerTool}. Do not migrate it via migrate_reminder; use ${managerTool} instead.`,
|
|
375
464
|
invalidFormatDoMind: 'Error: Invalid args. Use: do_mind({ selector: string, category?: string, content: string })',
|
|
376
465
|
invalidFormatChangeMind: 'Error: Invalid args. Use: change_mind({ selector: string, category?: string, content: string, previous_content_hash: string })',
|
|
377
466
|
tooManyArgsChangeMind: 'Error: Invalid args. Use: change_mind({ selector: string, category?: string, content: string, previous_content_hash: string })',
|
|
@@ -456,7 +545,7 @@ exports.addReminderTool = {
|
|
|
456
545
|
description: 'Add a manually maintained reminder for current work. Scope defaults to task so the reminder survives continuing the same Taskdoc in another dialog; dialog is only for truly dialog-local notes; agent is visible to this agent across dialogs and should be reserved for urgent short-lived global cues. Do not manually record runtime-maintained environment state such as background process status or in-flight background asks.',
|
|
457
546
|
descriptionI18n: {
|
|
458
547
|
en: 'Add a manually maintained reminder for current work. Scope defaults to task so the reminder survives continuing the same Taskdoc in another dialog; dialog is only for truly dialog-local notes; agent is visible to this agent across dialogs and should be reserved for urgent short-lived global cues. Do not manually record runtime-maintained environment state such as background process status or in-flight background asks.',
|
|
459
|
-
zh: '添加手工维护的手头工作提醒。scope 默认 task,以便同一差遣牒任务换新对话继续时仍可见;dialog 只用于真正对话局部的事项;agent
|
|
548
|
+
zh: '添加手工维护的手头工作提醒。scope 默认 task,以便同一差遣牒任务换新对话继续时仍可见;dialog 只用于真正对话局部的事项;agent 会在本智能体后续对话中继续可见,仅用于紧急、短期、全局刺眼提醒。不要手工记录后台进程状态、后台进行中诉请等 runtime 会自动维护的环境状态。',
|
|
460
549
|
},
|
|
461
550
|
parameters: {
|
|
462
551
|
type: 'object',
|
|
@@ -592,12 +681,20 @@ exports.updateReminderTool = {
|
|
|
592
681
|
const updated = await updateResolvedReminderTarget(dlg, resolved.target, reminderContent, stripResult.nextMeta, reminderRenderMode);
|
|
593
682
|
if (!updated)
|
|
594
683
|
return (0, tool_1.toolFailure)(t.reminderTargetChanged);
|
|
595
|
-
return
|
|
684
|
+
return await formatUpdateReminderSuccessResult({
|
|
685
|
+
dlg,
|
|
686
|
+
target: resolved.target,
|
|
687
|
+
language,
|
|
688
|
+
});
|
|
596
689
|
}
|
|
597
690
|
const updated = await updateResolvedReminderTarget(dlg, resolved.target, reminderContent, undefined, reminderRenderMode);
|
|
598
691
|
if (!updated)
|
|
599
692
|
return (0, tool_1.toolFailure)(t.reminderTargetChanged);
|
|
600
|
-
return
|
|
693
|
+
return await formatUpdateReminderSuccessResult({
|
|
694
|
+
dlg,
|
|
695
|
+
target: resolved.target,
|
|
696
|
+
language,
|
|
697
|
+
});
|
|
601
698
|
}
|
|
602
699
|
const reminderMeta = buildContinuationPackageReminderMeta({
|
|
603
700
|
existingMeta: reminder?.meta,
|
|
@@ -607,7 +704,84 @@ exports.updateReminderTool = {
|
|
|
607
704
|
const updated = await updateResolvedReminderTarget(dlg, resolved.target, reminderContent, reminderMeta, reminderRenderMode);
|
|
608
705
|
if (!updated)
|
|
609
706
|
return (0, tool_1.toolFailure)(t.reminderTargetChanged);
|
|
610
|
-
return
|
|
707
|
+
return await formatUpdateReminderSuccessResult({
|
|
708
|
+
dlg,
|
|
709
|
+
target: resolved.target,
|
|
710
|
+
language,
|
|
711
|
+
});
|
|
712
|
+
},
|
|
713
|
+
};
|
|
714
|
+
exports.migrateReminderTool = {
|
|
715
|
+
type: 'func',
|
|
716
|
+
name: 'migrate_reminder',
|
|
717
|
+
description: 'Move a visible shared reminder back into the current dialog scope. Use this after a task/agent shared reminder update turns out to belong only to the updater dialog, so Dominds withdraws it from affected parallel dialogs.',
|
|
718
|
+
descriptionI18n: {
|
|
719
|
+
en: 'Move a visible shared reminder back into the current dialog scope. Use this after a task/agent shared reminder update turns out to belong only to the updater dialog, so Dominds withdraws it from affected parallel dialogs.',
|
|
720
|
+
zh: '把当前可见的共享提醒项迁回当前对话范围。用于 task/agent 共享提醒更新后发现内容只属于更新者对话时,将它从受影响并行对话中撤下。',
|
|
721
|
+
},
|
|
722
|
+
parameters: {
|
|
723
|
+
type: 'object',
|
|
724
|
+
additionalProperties: false,
|
|
725
|
+
required: ['reminder_id', 'scope'],
|
|
726
|
+
properties: {
|
|
727
|
+
reminder_id: { type: 'string', description: 'Stable reminder id.' },
|
|
728
|
+
scope: {
|
|
729
|
+
type: 'string',
|
|
730
|
+
enum: ['dialog'],
|
|
731
|
+
description: 'Target scope. Currently only dialog is supported.',
|
|
732
|
+
},
|
|
733
|
+
},
|
|
734
|
+
},
|
|
735
|
+
argsValidation: 'dominds',
|
|
736
|
+
async call(dlg, _caller, args) {
|
|
737
|
+
const language = (0, work_language_1.getWorkLanguage)();
|
|
738
|
+
const t = getCtrlMessages(language);
|
|
739
|
+
if (args['scope'] !== 'dialog') {
|
|
740
|
+
return (0, tool_1.toolFailure)(t.invalidFormatMigrate);
|
|
741
|
+
}
|
|
742
|
+
const resolved = await resolveReminderTarget(dlg, args['reminder_id']);
|
|
743
|
+
if (!resolved.ok) {
|
|
744
|
+
const reminderId = resolved.reminderId.trim();
|
|
745
|
+
if (reminderId === '')
|
|
746
|
+
return (0, tool_1.toolFailure)(t.invalidFormatMigrate);
|
|
747
|
+
return (0, tool_1.toolFailure)(t.reminderDoesNotExist(reminderId));
|
|
748
|
+
}
|
|
749
|
+
const targetReminder = resolved.target.reminder;
|
|
750
|
+
if (resolved.target.source === 'dialog') {
|
|
751
|
+
return (0, tool_1.toolSuccess)(t.reminderAlreadyDialogScope(targetReminder.id));
|
|
752
|
+
}
|
|
753
|
+
const deleteAltInstruction = getDeleteAltInstruction(targetReminder.meta);
|
|
754
|
+
if (deleteAltInstruction !== undefined) {
|
|
755
|
+
return (0, tool_1.toolFailure)(formatManualDeleteBlockedError(language, deleteAltInstruction));
|
|
756
|
+
}
|
|
757
|
+
const managerTool = getManagerTool(targetReminder.meta);
|
|
758
|
+
if (managerTool !== undefined) {
|
|
759
|
+
return (0, tool_1.toolFailure)(t.reminderMigrateManagedBlocked(managerTool));
|
|
760
|
+
}
|
|
761
|
+
if (targetReminder.owner?.updateReminder !== undefined) {
|
|
762
|
+
return (0, tool_1.toolFailure)(language === 'zh'
|
|
763
|
+
? '错误:该提醒项由 reminder owner 自动维护,不能用 migrate_reminder 迁移。'
|
|
764
|
+
: 'Error: This reminder is automatically maintained by a reminder owner and cannot be migrated via migrate_reminder.');
|
|
765
|
+
}
|
|
766
|
+
const scope = resolveSharedReminderUpdateImpactScope(resolved.target);
|
|
767
|
+
const migrated = await migrateSharedReminderTargetToDialog({
|
|
768
|
+
dlg,
|
|
769
|
+
target: resolved.target,
|
|
770
|
+
});
|
|
771
|
+
if (!migrated)
|
|
772
|
+
return (0, tool_1.toolFailure)(t.reminderTargetChanged);
|
|
773
|
+
const baseOutput = (0, tool_1.toolSuccess)(language === 'zh'
|
|
774
|
+
? `已迁移:reminder_id=${targetReminder.id} 已从共享范围撤下,并保留为当前对话范围提醒项。`
|
|
775
|
+
: `Migrated: reminder_id=${targetReminder.id} has been withdrawn from shared scope and kept as a current-dialog reminder.`);
|
|
776
|
+
const dispatch = scope === undefined
|
|
777
|
+
? undefined
|
|
778
|
+
: await (0, shared_reminder_update_impact_1.dispatchSharedReminderMigrationImpact)({
|
|
779
|
+
updater: dlg,
|
|
780
|
+
reminderId: targetReminder.id,
|
|
781
|
+
scope,
|
|
782
|
+
language,
|
|
783
|
+
});
|
|
784
|
+
return appendSharedReminderMigrationImpactToToolResult(baseOutput, language, dispatch);
|
|
611
785
|
},
|
|
612
786
|
};
|
|
613
787
|
exports.clearMindTool = {
|
|
@@ -641,7 +815,10 @@ exports.clearMindTool = {
|
|
|
641
815
|
createdBy: 'clear_mind',
|
|
642
816
|
contextHealthLevel,
|
|
643
817
|
});
|
|
644
|
-
dlg.addReminder(reminderContent, undefined, continuationMeta
|
|
818
|
+
dlg.addReminder(reminderContent, undefined, continuationMeta, undefined, {
|
|
819
|
+
scope: 'dialog',
|
|
820
|
+
renderMode: 'markdown',
|
|
821
|
+
});
|
|
645
822
|
}
|
|
646
823
|
await dlg.startNewCourse(t.clearedCoursePrompt(dlg.currentCourse + 1));
|
|
647
824
|
// Context health snapshot is inherently tied to the previous prompt/context.
|
package/dist/tools/os.js
CHANGED
|
@@ -24,6 +24,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
24
24
|
const module_1 = require("module");
|
|
25
25
|
const net_1 = __importDefault(require("net"));
|
|
26
26
|
const path_1 = __importDefault(require("path"));
|
|
27
|
+
const url_1 = require("url");
|
|
27
28
|
const util_1 = require("util");
|
|
28
29
|
const rtws_1 = require("../rtws");
|
|
29
30
|
const driver_messages_1 = require("../runtime/driver-messages");
|
|
@@ -572,6 +573,8 @@ function getOsToolMessages(language) {
|
|
|
572
573
|
daemonOutputHeader: (pid, streamLabel) => `📤 守护进程 ${pid} ${streamLabel} 输出:\n`,
|
|
573
574
|
noOutput: '(无输出)',
|
|
574
575
|
scrolledOutNotice: (lines) => `\n\n⚠️ 有 ${lines} 行已滚出可视范围`,
|
|
576
|
+
daemonOutputWaitTimedOut: (pid, streamLabel, timeoutMs) => `⏱️ 等待守护进程 ${pid} 的 ${streamLabel} 新输出已超时(timeout_ms=${timeoutMs})。以下是当前快照。\n\n`,
|
|
577
|
+
daemonOutputWaitExited: (pid, streamLabel) => `⏹️ 等待守护进程 ${pid} 的 ${streamLabel} 新输出期间进程已退出;未观察到新的所选流输出。以下是最终快照。\n\n`,
|
|
575
578
|
};
|
|
576
579
|
}
|
|
577
580
|
return {
|
|
@@ -587,6 +590,8 @@ function getOsToolMessages(language) {
|
|
|
587
590
|
daemonOutputHeader: (pid, streamLabel) => `📤 Daemon ${pid} ${streamLabel} output:\n`,
|
|
588
591
|
noOutput: '(no output)',
|
|
589
592
|
scrolledOutNotice: (lines) => `\n\n⚠️ ${lines} lines have scrolled out of view`,
|
|
593
|
+
daemonOutputWaitTimedOut: (pid, streamLabel, timeoutMs) => `⏱️ Timed out waiting for new ${streamLabel} output from daemon ${pid} (timeout_ms=${timeoutMs}). Current snapshot follows.\n\n`,
|
|
594
|
+
daemonOutputWaitExited: (pid, streamLabel) => `⏹️ Daemon ${pid} exited while waiting for new ${streamLabel} output; no new requested-stream output was observed. Final snapshot follows.\n\n`,
|
|
590
595
|
};
|
|
591
596
|
}
|
|
592
597
|
function disconnectRunnerProcess(runnerProcess) {
|
|
@@ -818,12 +823,38 @@ function parseGetDaemonOutputArgs(args) {
|
|
|
818
823
|
if (stderrRaw !== undefined && typeof stderrRaw !== 'boolean') {
|
|
819
824
|
throw new Error('get_daemon_output.stderr must be a boolean if provided');
|
|
820
825
|
}
|
|
826
|
+
const waitForNewOutputRaw = args.wait_for_new_output;
|
|
827
|
+
if (waitForNewOutputRaw !== undefined && typeof waitForNewOutputRaw !== 'boolean') {
|
|
828
|
+
throw new Error('get_daemon_output.wait_for_new_output must be a boolean if provided');
|
|
829
|
+
}
|
|
830
|
+
const timeoutMsRaw = args.timeout_ms;
|
|
831
|
+
if (timeoutMsRaw !== undefined && typeof timeoutMsRaw !== 'number') {
|
|
832
|
+
throw new Error('get_daemon_output.timeout_ms must be a number if provided');
|
|
833
|
+
}
|
|
834
|
+
const timeoutMs = timeoutMsRaw === undefined
|
|
835
|
+
? undefined
|
|
836
|
+
: Number.isInteger(timeoutMsRaw) &&
|
|
837
|
+
timeoutMsRaw >= 0 &&
|
|
838
|
+
timeoutMsRaw <= cmd_runner_protocol_1.MAX_CMD_RUNNER_OUTPUT_WAIT_TIMEOUT_MS
|
|
839
|
+
? timeoutMsRaw
|
|
840
|
+
: (() => {
|
|
841
|
+
throw new Error(`get_daemon_output.timeout_ms must be a non-negative integer <= ${String(cmd_runner_protocol_1.MAX_CMD_RUNNER_OUTPUT_WAIT_TIMEOUT_MS)}`);
|
|
842
|
+
})();
|
|
821
843
|
const stdout = stdoutRaw ?? true;
|
|
822
844
|
const stderr = stderrRaw ?? true;
|
|
823
845
|
if (!stdout && !stderr) {
|
|
824
846
|
throw new Error('get_daemon_output requires at least one of stdout/stderr to be true');
|
|
825
847
|
}
|
|
826
|
-
|
|
848
|
+
if (waitForNewOutputRaw === false && timeoutMs !== undefined) {
|
|
849
|
+
throw new Error('get_daemon_output.timeout_ms cannot be provided when wait_for_new_output is false');
|
|
850
|
+
}
|
|
851
|
+
return {
|
|
852
|
+
pid,
|
|
853
|
+
stdout,
|
|
854
|
+
stderr,
|
|
855
|
+
waitForNewOutput: waitForNewOutputRaw ?? timeoutMs !== undefined,
|
|
856
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
857
|
+
};
|
|
827
858
|
}
|
|
828
859
|
function getWindowsShellLabel(shell) {
|
|
829
860
|
if (typeof shell !== 'string') {
|
|
@@ -945,8 +976,8 @@ function resolveCmdRunnerEntrypointAbs() {
|
|
|
945
976
|
}
|
|
946
977
|
const tsCandidate = path_1.default.resolve(__dirname, 'cmd-runner.ts');
|
|
947
978
|
if (fs_1.default.existsSync(tsCandidate)) {
|
|
948
|
-
const
|
|
949
|
-
return { ok: true, scriptAbs: tsCandidate, execArgv: ['--import',
|
|
979
|
+
const tsxLoaderUrl = (0, url_1.pathToFileURL)(requireFn.resolve('tsx')).href;
|
|
980
|
+
return { ok: true, scriptAbs: tsCandidate, execArgv: ['--import', tsxLoaderUrl] };
|
|
950
981
|
}
|
|
951
982
|
return {
|
|
952
983
|
ok: false,
|
|
@@ -991,21 +1022,30 @@ async function callRunner(endpoint, request, timeoutMs = 5_000) {
|
|
|
991
1022
|
if (settled)
|
|
992
1023
|
return;
|
|
993
1024
|
settled = true;
|
|
994
|
-
|
|
1025
|
+
if (timeoutHandle !== undefined) {
|
|
1026
|
+
clearTimeout(timeoutHandle);
|
|
1027
|
+
}
|
|
995
1028
|
socket.destroy();
|
|
996
1029
|
fn();
|
|
997
1030
|
};
|
|
998
|
-
const timeoutHandle =
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1031
|
+
const timeoutHandle = timeoutMs === null
|
|
1032
|
+
? undefined
|
|
1033
|
+
: setTimeout(() => {
|
|
1034
|
+
finalize(() => {
|
|
1035
|
+
reject(new Error(`cmd_runner request timed out for endpoint ${endpoint}`));
|
|
1036
|
+
});
|
|
1037
|
+
}, timeoutMs);
|
|
1003
1038
|
socket.setEncoding('utf8');
|
|
1004
1039
|
socket.once('error', (error) => {
|
|
1005
1040
|
finalize(() => {
|
|
1006
1041
|
reject(error);
|
|
1007
1042
|
});
|
|
1008
1043
|
});
|
|
1044
|
+
socket.once('close', () => {
|
|
1045
|
+
finalize(() => {
|
|
1046
|
+
reject(new Error(`cmd_runner connection closed before response for endpoint ${endpoint}`));
|
|
1047
|
+
});
|
|
1048
|
+
});
|
|
1009
1049
|
socket.on('data', (chunk) => {
|
|
1010
1050
|
buffer += chunk;
|
|
1011
1051
|
const newlineIndex = buffer.indexOf('\n');
|
|
@@ -1101,6 +1141,45 @@ async function resolveDaemonFromMeta(meta) {
|
|
|
1101
1141
|
};
|
|
1102
1142
|
}
|
|
1103
1143
|
}
|
|
1144
|
+
function getRunnerRequestTimeoutForDaemonOutput(args) {
|
|
1145
|
+
if (!args.waitForNewOutput) {
|
|
1146
|
+
return undefined;
|
|
1147
|
+
}
|
|
1148
|
+
if (args.timeoutMs !== undefined) {
|
|
1149
|
+
return Math.max(5_000, args.timeoutMs + 5_000);
|
|
1150
|
+
}
|
|
1151
|
+
return null;
|
|
1152
|
+
}
|
|
1153
|
+
async function resolveDaemonOutputFromMeta(meta, args) {
|
|
1154
|
+
if (meta.runnerEndpoint !== undefined && meta.runnerEndpoint.trim() !== '') {
|
|
1155
|
+
try {
|
|
1156
|
+
const response = await callRunner(meta.runnerEndpoint, {
|
|
1157
|
+
type: 'get_output',
|
|
1158
|
+
stdout: args.stdout,
|
|
1159
|
+
stderr: args.stderr,
|
|
1160
|
+
waitForNewOutput: args.waitForNewOutput,
|
|
1161
|
+
...(args.timeoutMs === undefined ? {} : { timeoutMs: args.timeoutMs }),
|
|
1162
|
+
}, getRunnerRequestTimeoutForDaemonOutput(args));
|
|
1163
|
+
if (response.ok &&
|
|
1164
|
+
response.type === 'output' &&
|
|
1165
|
+
runnerResponseMatchesReminder(meta, response)) {
|
|
1166
|
+
return {
|
|
1167
|
+
kind: 'live',
|
|
1168
|
+
daemon: buildRunnerBackedDaemon(meta, response),
|
|
1169
|
+
...(response.waitStatus === undefined ? {} : { waitStatus: response.waitStatus }),
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
catch {
|
|
1174
|
+
// Fall through to stale-or-gone detection.
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
const resolved = await resolveDaemonFromMeta(meta);
|
|
1178
|
+
if (resolved.kind !== 'live') {
|
|
1179
|
+
return resolved;
|
|
1180
|
+
}
|
|
1181
|
+
return { kind: 'live', daemon: resolved.daemon };
|
|
1182
|
+
}
|
|
1104
1183
|
function formatRunnerBackedDaemonStatusDetails(daemon, language) {
|
|
1105
1184
|
const uptime = Math.floor((Date.now() - daemon.startTime.getTime()) / 1000);
|
|
1106
1185
|
const status = language === 'zh'
|
|
@@ -1248,6 +1327,14 @@ const getDaemonOutputSchema = {
|
|
|
1248
1327
|
type: 'boolean',
|
|
1249
1328
|
description: 'Whether to include stderr output (default: true unless stdout is explicitly set)',
|
|
1250
1329
|
},
|
|
1330
|
+
wait_for_new_output: {
|
|
1331
|
+
type: 'boolean',
|
|
1332
|
+
description: 'Whether to wait until at least one requested stream receives new output before returning (default: false, or true when timeout_ms is provided)',
|
|
1333
|
+
},
|
|
1334
|
+
timeout_ms: {
|
|
1335
|
+
type: 'integer',
|
|
1336
|
+
description: 'Optional non-negative maximum time in milliseconds to wait for new output, up to 86400000 (24h); providing this implies wait_for_new_output=true unless wait_for_new_output is explicitly false, which is rejected',
|
|
1337
|
+
},
|
|
1251
1338
|
},
|
|
1252
1339
|
required: ['pid'],
|
|
1253
1340
|
additionalProperties: false,
|
|
@@ -2788,26 +2875,33 @@ exports.stopDaemonTool = {
|
|
|
2788
2875
|
}
|
|
2789
2876
|
},
|
|
2790
2877
|
};
|
|
2878
|
+
function formatRequestedDaemonOutputStreams(stdout, stderr) {
|
|
2879
|
+
if (stdout && stderr) {
|
|
2880
|
+
return 'stdout/stderr';
|
|
2881
|
+
}
|
|
2882
|
+
return stdout ? 'stdout' : 'stderr';
|
|
2883
|
+
}
|
|
2791
2884
|
// Get daemon output tool implementation
|
|
2792
2885
|
exports.getDaemonOutputTool = {
|
|
2793
2886
|
type: 'func',
|
|
2794
2887
|
name: 'get_daemon_output',
|
|
2795
|
-
description: 'Retrieve captured stdout/stderr output from a tracked daemon process by PID. By default both streams are returned together; you may disable either stream explicitly. Returns (no output) if a requested stream has not produced output yet.',
|
|
2888
|
+
description: 'Retrieve captured stdout/stderr output from a tracked daemon process by PID. By default both streams are returned together; you may disable either stream explicitly. Set wait_for_new_output=true to wait until a requested stream receives new output before returning; timeout_ms optionally bounds that wait. Returns (no output) if a requested stream has not produced output yet.',
|
|
2796
2889
|
descriptionI18n: {
|
|
2797
|
-
en: 'Retrieve captured stdout/stderr output from a tracked daemon process by PID. By default both streams are returned together; you may disable either stream explicitly. Returns (no output) if a requested stream has not produced output yet.',
|
|
2798
|
-
zh: '根据 PID 获取已追踪守护进程的 stdout/stderr
|
|
2890
|
+
en: 'Retrieve captured stdout/stderr output from a tracked daemon process by PID. By default both streams are returned together; you may disable either stream explicitly. Set wait_for_new_output=true to wait until a requested stream receives new output before returning; timeout_ms optionally bounds that wait. Returns (no output) if a requested stream has not produced output yet.',
|
|
2891
|
+
zh: '根据 PID 获取已追踪守护进程的 stdout/stderr 输出。默认会同时返回两个流,也可显式关闭其中一个;设置 wait_for_new_output=true 时会等到所请求流出现新输出后再返回,timeout_ms 可选用于限制等待毫秒数;若所请求的流尚无输出,则返回 (no output)。',
|
|
2799
2892
|
},
|
|
2800
2893
|
parameters: getDaemonOutputSchema,
|
|
2801
2894
|
async call(dlg, caller, args) {
|
|
2802
2895
|
const language = (0, work_language_1.getWorkLanguage)();
|
|
2803
2896
|
const t = getOsToolMessages(language);
|
|
2804
|
-
const
|
|
2897
|
+
const parsedArgs = parseGetDaemonOutputArgs(args);
|
|
2898
|
+
const { pid, stdout, stderr } = parsedArgs;
|
|
2805
2899
|
const reminders = await (0, shared_reminders_1.loadSharedReminders)({ kind: 'agent', agentId: dlg.agentId });
|
|
2806
2900
|
const reminder = reminders.find((candidate) => isShellCmdReminder(candidate) && candidate.meta.pid === pid);
|
|
2807
2901
|
if (!reminder || !isShellCmdReminder(reminder)) {
|
|
2808
2902
|
return (0, tool_1.toolFailure)(t.noDaemonFound(pid));
|
|
2809
2903
|
}
|
|
2810
|
-
const resolved = await
|
|
2904
|
+
const resolved = await resolveDaemonOutputFromMeta(reminder.meta, parsedArgs);
|
|
2811
2905
|
if (resolved.kind === 'gone') {
|
|
2812
2906
|
await removeDaemonRemindersForPid(dlg, pid);
|
|
2813
2907
|
return (0, tool_1.toolFailure)(t.noDaemonFound(pid));
|
|
@@ -2819,6 +2913,13 @@ exports.getDaemonOutputTool = {
|
|
|
2819
2913
|
let result = '';
|
|
2820
2914
|
const fenceConsole = '```console';
|
|
2821
2915
|
const fenceEnd = '```';
|
|
2916
|
+
const requestedStreams = formatRequestedDaemonOutputStreams(stdout, stderr);
|
|
2917
|
+
if (resolved.waitStatus === 'timeout') {
|
|
2918
|
+
result += t.daemonOutputWaitTimedOut(pid, requestedStreams, parsedArgs.timeoutMs ?? 0);
|
|
2919
|
+
}
|
|
2920
|
+
else if (resolved.waitStatus === 'exited') {
|
|
2921
|
+
result += t.daemonOutputWaitExited(pid, requestedStreams);
|
|
2922
|
+
}
|
|
2822
2923
|
if (stdout) {
|
|
2823
2924
|
const stdoutContent = (0, output_limit_1.truncateToolOutputText)(daemon.stdoutContent, {
|
|
2824
2925
|
toolName: 'get_daemon_output_stdout',
|
|
@@ -30,10 +30,59 @@ async function bestEffortTaskkill(pid, force) {
|
|
|
30
30
|
// Best effort only.
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
async function bestEffortListWindowsDescendantPids(pid) {
|
|
34
|
+
assertValidPid(pid);
|
|
35
|
+
try {
|
|
36
|
+
const command = `
|
|
37
|
+
$processes = Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId
|
|
38
|
+
$frontier = @(${String(pid)})
|
|
39
|
+
$result = @()
|
|
40
|
+
while ($frontier.Count -gt 0) {
|
|
41
|
+
$next = @()
|
|
42
|
+
foreach ($parentPid in $frontier) {
|
|
43
|
+
foreach ($child in $processes | Where-Object { $_.ParentProcessId -eq $parentPid }) {
|
|
44
|
+
$childPid = [int]$child.ProcessId
|
|
45
|
+
$result += $childPid
|
|
46
|
+
$next += $childPid
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
$frontier = $next
|
|
50
|
+
}
|
|
51
|
+
$result | ForEach-Object { [Console]::Out.WriteLine($_) }
|
|
52
|
+
`;
|
|
53
|
+
const { stdout } = await execFileAsync('powershell.exe', ['-NoProfile', '-Command', command], {
|
|
54
|
+
windowsHide: true,
|
|
55
|
+
maxBuffer: 1024 * 1024,
|
|
56
|
+
});
|
|
57
|
+
const parsed = [];
|
|
58
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
59
|
+
const trimmed = line.trim();
|
|
60
|
+
if (trimmed === '') {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const parsedPid = Number(trimmed);
|
|
64
|
+
if (Number.isInteger(parsedPid) && parsedPid > 0) {
|
|
65
|
+
parsed.push(parsedPid);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return parsed;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
33
74
|
async function bestEffortKillWindowsProcessTree(pid) {
|
|
34
75
|
assertValidPid(pid);
|
|
76
|
+
const descendantPids = await bestEffortListWindowsDescendantPids(pid);
|
|
77
|
+
for (const descendantPid of descendantPids.slice().reverse()) {
|
|
78
|
+
await bestEffortTaskkill(descendantPid, false);
|
|
79
|
+
}
|
|
35
80
|
await bestEffortTaskkill(pid, false);
|
|
36
81
|
await sleepMs(1_000);
|
|
82
|
+
for (const descendantPid of descendantPids.slice().reverse()) {
|
|
83
|
+
await bestEffortTaskkill(descendantPid, true);
|
|
84
|
+
bestEffortKillPid(descendantPid);
|
|
85
|
+
}
|
|
37
86
|
await bestEffortTaskkill(pid, true);
|
|
38
87
|
bestEffortKillPid(pid);
|
|
39
88
|
}
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
|
|
74
74
|
### Q: What's the difference between dialog, task, agent reminders, and memory?
|
|
75
75
|
|
|
76
|
-
A: `dialog` reminders are only for the current dialog's current work. `task` reminders are for current work under the same Taskdoc and are the default for `add_reminder`. `agent` reminders stay visible in
|
|
76
|
+
A: `dialog` reminders are only for the current dialog's current work. `task` reminders are for current work under the same Taskdoc and are the default for `add_reminder`. `agent` reminders stay visible in later dialogs you lead, but only for urgent, short-lived, globally visible cues. None of them are long-term knowledge. `personal_memory` is for durable facts and reusable knowledge saved to disk; if the information should synchronize the team's current effective state, key decisions, next step, or still-active blockers, write it to Taskdoc `progress` instead of a reminder.
|
|
77
77
|
|
|
78
78
|
### Q: How do I choose `dialog`, `task`, and `agent`?
|
|
79
79
|
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
control is Dominds' **dialog control toolset** for managing dialog state, reminders, taskdocs, and inter-dialog reply closure semantics:
|
|
28
28
|
|
|
29
|
-
- **Reminder management**: Three reminder scopes: `dialog` / `task` / `agent`. Default to `task` for current work under the same Taskdoc; use `dialog`
|
|
29
|
+
- **Reminder management**: Three reminder scopes: `dialog` / `task` / `agent`. Default to `task` for ordinary current work under the same Taskdoc; use `dialog` for truly dialog-local notes, and continuation packages before `clear_mind` must explicitly use `scope=dialog` and state this dialog task goal; use `agent` only for urgent, short-lived, globally visible cues
|
|
30
30
|
- **Taskdoc operations**: Append to, replace, or delete task contract sections (goals/constraints/progress); within Taskdoc, `progress` is the team-shared, quasi-real-time, scannable task bulletin board
|
|
31
31
|
- **Context maintenance**: Reduce cognitive load without losing key resume state
|
|
32
32
|
- **Reply routing**: Separate asking the tellasker back, sending the final reply, and ordinary plain text in Side Dialog / ask-back flows
|
|
@@ -56,15 +56,15 @@ Reminders are temporary current-work information for:
|
|
|
56
56
|
- Marking pending tasks
|
|
57
57
|
- Tracking current next steps / blockers
|
|
58
58
|
- Recording blocking issues
|
|
59
|
-
- Holding continuation-package bridge notes before `clear_mind`
|
|
59
|
+
- Holding current-dialog scoped continuation-package bridge notes before `clear_mind`
|
|
60
60
|
|
|
61
61
|
Reminders are not for manually copying environment state automatically maintained by Dominds, such as background process status, in-flight background asks/collaboration, or browser/session attachment state. Dominds-managed reminders, panels, and tool outputs are the authoritative place for that state; manual notes go stale easily and create cognitive noise.
|
|
62
62
|
|
|
63
63
|
Scope rule:
|
|
64
64
|
|
|
65
|
-
- `dialog`: current-dialog current work
|
|
65
|
+
- `dialog`: current-dialog current work; continuation packages before `clear_mind` must explicitly use this scope and state this dialog task goal
|
|
66
66
|
- `task`: current work under the current Taskdoc, and the default scope
|
|
67
|
-
- `agent`: urgent, short-lived, globally visible cues you should keep seeing
|
|
67
|
+
- `agent`: urgent, short-lived, globally visible cues you should keep seeing across later dialogs you lead
|
|
68
68
|
|
|
69
69
|
### Taskdoc
|
|
70
70
|
|