dominds 1.25.9 → 1.25.11
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/llm/kernel-driver/drive.js +234 -34
- package/dist/llm/kernel-driver/fbr.js +2 -0
- package/dist/llm/kernel-driver/tellask-special.d.ts +1 -0
- package/dist/llm/kernel-driver/tellask-special.js +6 -1
- package/dist/server/dominds-self-update.d.ts +1 -0
- package/dist/server/dominds-self-update.js +63 -5
- package/dist/server/websocket-handler.js +31 -2
- package/dist/server.js +13 -0
- package/dist/tools/ctrl.js +1 -0
- package/package.json +3 -3
- package/webapp/dist/assets/{_basePickBy-B4PyBhGX.js → _basePickBy-BF9Zg9uq.js} +3 -3
- package/webapp/dist/assets/{_basePickBy-B4PyBhGX.js.map → _basePickBy-BF9Zg9uq.js.map} +1 -1
- package/webapp/dist/assets/{_baseUniq-Cj1Wg2fB.js → _baseUniq-CFjISsgz.js} +2 -2
- package/webapp/dist/assets/{_baseUniq-Cj1Wg2fB.js.map → _baseUniq-CFjISsgz.js.map} +1 -1
- package/webapp/dist/assets/{arc-CWrSn-79.js → arc-BVyYGzE7.js} +2 -2
- package/webapp/dist/assets/{arc-CWrSn-79.js.map → arc-BVyYGzE7.js.map} +1 -1
- package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-7iHYM2Z2.js → architectureDiagram-2XIMDMQ5-SEmNTU1b.js} +7 -7
- package/webapp/dist/assets/{architectureDiagram-2XIMDMQ5-7iHYM2Z2.js.map → architectureDiagram-2XIMDMQ5-SEmNTU1b.js.map} +1 -1
- package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-B1TGBHuF.js → blockDiagram-WCTKOSBZ-BndD4gLF.js} +7 -7
- package/webapp/dist/assets/{blockDiagram-WCTKOSBZ-B1TGBHuF.js.map → blockDiagram-WCTKOSBZ-BndD4gLF.js.map} +1 -1
- package/webapp/dist/assets/{c4Diagram-IC4MRINW-BXTEEF9u.js → c4Diagram-IC4MRINW-fGAz7umu.js} +3 -3
- package/webapp/dist/assets/{c4Diagram-IC4MRINW-BXTEEF9u.js.map → c4Diagram-IC4MRINW-fGAz7umu.js.map} +1 -1
- package/webapp/dist/assets/{channel-C9f2l1ub.js → channel-Blt7S1Sn.js} +2 -2
- package/webapp/dist/assets/{channel-C9f2l1ub.js.map → channel-Blt7S1Sn.js.map} +1 -1
- package/webapp/dist/assets/{chunk-4BX2VUAB-CXrwbryc.js → chunk-4BX2VUAB-C2FKcyob.js} +2 -2
- package/webapp/dist/assets/{chunk-4BX2VUAB-CXrwbryc.js.map → chunk-4BX2VUAB-C2FKcyob.js.map} +1 -1
- package/webapp/dist/assets/{chunk-55IACEB6-WAoUKKa8.js → chunk-55IACEB6-CN8ZmdUP.js} +2 -2
- package/webapp/dist/assets/{chunk-55IACEB6-WAoUKKa8.js.map → chunk-55IACEB6-CN8ZmdUP.js.map} +1 -1
- package/webapp/dist/assets/{chunk-FMBD7UC4-CP1cvzbf.js → chunk-FMBD7UC4-B9Uq2tt2.js} +2 -2
- package/webapp/dist/assets/{chunk-FMBD7UC4-CP1cvzbf.js.map → chunk-FMBD7UC4-B9Uq2tt2.js.map} +1 -1
- package/webapp/dist/assets/{chunk-JSJVCQXG-Od7hQgy7.js → chunk-JSJVCQXG-vVrXi8LV.js} +2 -2
- package/webapp/dist/assets/{chunk-JSJVCQXG-Od7hQgy7.js.map → chunk-JSJVCQXG-vVrXi8LV.js.map} +1 -1
- package/webapp/dist/assets/{chunk-KX2RTZJC-Cp53YoAm.js → chunk-KX2RTZJC-DtZmdBq3.js} +2 -2
- package/webapp/dist/assets/{chunk-KX2RTZJC-Cp53YoAm.js.map → chunk-KX2RTZJC-DtZmdBq3.js.map} +1 -1
- package/webapp/dist/assets/{chunk-NQ4KR5QH-DkyaPUwI.js → chunk-NQ4KR5QH-C3rU1XEw.js} +4 -4
- package/webapp/dist/assets/{chunk-NQ4KR5QH-DkyaPUwI.js.map → chunk-NQ4KR5QH-C3rU1XEw.js.map} +1 -1
- package/webapp/dist/assets/{chunk-QZHKN3VN-DPcrZWPF.js → chunk-QZHKN3VN-CeHQK_vs.js} +2 -2
- package/webapp/dist/assets/{chunk-QZHKN3VN-DPcrZWPF.js.map → chunk-QZHKN3VN-CeHQK_vs.js.map} +1 -1
- package/webapp/dist/assets/{chunk-WL4C6EOR-BCx3EtFm.js → chunk-WL4C6EOR-Bb8GUwSo.js} +6 -6
- package/webapp/dist/assets/{chunk-WL4C6EOR-BCx3EtFm.js.map → chunk-WL4C6EOR-Bb8GUwSo.js.map} +1 -1
- package/webapp/dist/assets/{classDiagram-VBA2DB6C-BnwaVhDD.js → classDiagram-VBA2DB6C-CmP7X8Fj.js} +7 -7
- package/webapp/dist/assets/{classDiagram-VBA2DB6C-BnwaVhDD.js.map → classDiagram-VBA2DB6C-CmP7X8Fj.js.map} +1 -1
- package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-BnwaVhDD.js → classDiagram-v2-RAHNMMFH-CmP7X8Fj.js} +7 -7
- package/webapp/dist/assets/{classDiagram-v2-RAHNMMFH-BnwaVhDD.js.map → classDiagram-v2-RAHNMMFH-CmP7X8Fj.js.map} +1 -1
- package/webapp/dist/assets/{clone-B32zbBXk.js → clone-CzGKO47U.js} +2 -2
- package/webapp/dist/assets/{clone-B32zbBXk.js.map → clone-CzGKO47U.js.map} +1 -1
- package/webapp/dist/assets/{cose-bilkent-S5V4N54A-DexuXMat.js → cose-bilkent-S5V4N54A-3wbordhk.js} +2 -2
- package/webapp/dist/assets/{cose-bilkent-S5V4N54A-DexuXMat.js.map → cose-bilkent-S5V4N54A-3wbordhk.js.map} +1 -1
- package/webapp/dist/assets/{dagre-KLK3FWXG-D-3S_ma4.js → dagre-KLK3FWXG-DrM7hFR5.js} +7 -7
- package/webapp/dist/assets/{dagre-KLK3FWXG-D-3S_ma4.js.map → dagre-KLK3FWXG-DrM7hFR5.js.map} +1 -1
- package/webapp/dist/assets/{diagram-E7M64L7V-CR7hat4A.js → diagram-E7M64L7V-D-bQEVk2.js} +8 -8
- package/webapp/dist/assets/{diagram-E7M64L7V-CR7hat4A.js.map → diagram-E7M64L7V-D-bQEVk2.js.map} +1 -1
- package/webapp/dist/assets/{diagram-IFDJBPK2-B6qnqfZU.js → diagram-IFDJBPK2-MaU_xQfR.js} +7 -7
- package/webapp/dist/assets/{diagram-IFDJBPK2-B6qnqfZU.js.map → diagram-IFDJBPK2-MaU_xQfR.js.map} +1 -1
- package/webapp/dist/assets/{diagram-P4PSJMXO-BDGFMbld.js → diagram-P4PSJMXO-BLklmc9h.js} +7 -7
- package/webapp/dist/assets/{diagram-P4PSJMXO-BDGFMbld.js.map → diagram-P4PSJMXO-BLklmc9h.js.map} +1 -1
- package/webapp/dist/assets/{erDiagram-INFDFZHY-BcXnNg1A.js → erDiagram-INFDFZHY-DHIVFsNv.js} +5 -5
- package/webapp/dist/assets/{erDiagram-INFDFZHY-BcXnNg1A.js.map → erDiagram-INFDFZHY-DHIVFsNv.js.map} +1 -1
- package/webapp/dist/assets/{flowDiagram-PKNHOUZH-CgKu7usU.js → flowDiagram-PKNHOUZH-HbMxSdg1.js} +7 -7
- package/webapp/dist/assets/{flowDiagram-PKNHOUZH-CgKu7usU.js.map → flowDiagram-PKNHOUZH-HbMxSdg1.js.map} +1 -1
- package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-BwkrQGcZ.js → ganttDiagram-A5KZAMGK-CxBETPNC.js} +3 -3
- package/webapp/dist/assets/{ganttDiagram-A5KZAMGK-BwkrQGcZ.js.map → ganttDiagram-A5KZAMGK-CxBETPNC.js.map} +1 -1
- package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-CVqi-T5p.js → gitGraphDiagram-K3NZZRJ6-DPV-fFTC.js} +8 -8
- package/webapp/dist/assets/{gitGraphDiagram-K3NZZRJ6-CVqi-T5p.js.map → gitGraphDiagram-K3NZZRJ6-DPV-fFTC.js.map} +1 -1
- package/webapp/dist/assets/{graph-B7VL6q5k.js → graph-C0MlCXJg.js} +3 -3
- package/webapp/dist/assets/{graph-B7VL6q5k.js.map → graph-C0MlCXJg.js.map} +1 -1
- package/webapp/dist/assets/{index-4xG_gPRh.js → index-CzHjX_nj.js} +168 -60
- package/webapp/dist/assets/{index-4xG_gPRh.js.map → index-CzHjX_nj.js.map} +1 -1
- package/webapp/dist/assets/{infoDiagram-LFFYTUFH-a9WBcgds.js → infoDiagram-LFFYTUFH-ChTC2kD-.js} +6 -6
- package/webapp/dist/assets/{infoDiagram-LFFYTUFH-a9WBcgds.js.map → infoDiagram-LFFYTUFH-ChTC2kD-.js.map} +1 -1
- package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-Bhiex3_9.js → ishikawaDiagram-PHBUUO56--aJd3LM6.js} +2 -2
- package/webapp/dist/assets/{ishikawaDiagram-PHBUUO56-Bhiex3_9.js.map → ishikawaDiagram-PHBUUO56--aJd3LM6.js.map} +1 -1
- package/webapp/dist/assets/{journeyDiagram-4ABVD52K-DdMDmgOQ.js → journeyDiagram-4ABVD52K-Bzd3cZTs.js} +5 -5
- package/webapp/dist/assets/{journeyDiagram-4ABVD52K-DdMDmgOQ.js.map → journeyDiagram-4ABVD52K-Bzd3cZTs.js.map} +1 -1
- package/webapp/dist/assets/{kanban-definition-K7BYSVSG-C02Ns-Ee.js → kanban-definition-K7BYSVSG-DiFHcs58.js} +3 -3
- package/webapp/dist/assets/{kanban-definition-K7BYSVSG-C02Ns-Ee.js.map → kanban-definition-K7BYSVSG-DiFHcs58.js.map} +1 -1
- package/webapp/dist/assets/{layout-CKFoJLnz.js → layout-B9Hsf17G.js} +5 -5
- package/webapp/dist/assets/{layout-CKFoJLnz.js.map → layout-B9Hsf17G.js.map} +1 -1
- package/webapp/dist/assets/{linear-DK87WR34.js → linear-6xqU78Yu.js} +2 -2
- package/webapp/dist/assets/{linear-DK87WR34.js.map → linear-6xqU78Yu.js.map} +1 -1
- package/webapp/dist/assets/{mindmap-definition-YRQLILUH-CNPEaAdT.js → mindmap-definition-YRQLILUH-BUI6M5up.js} +4 -4
- package/webapp/dist/assets/{mindmap-definition-YRQLILUH-CNPEaAdT.js.map → mindmap-definition-YRQLILUH-BUI6M5up.js.map} +1 -1
- package/webapp/dist/assets/{pieDiagram-SKSYHLDU-BAieZgqR.js → pieDiagram-SKSYHLDU-FCGp31Lg.js} +8 -8
- package/webapp/dist/assets/{pieDiagram-SKSYHLDU-BAieZgqR.js.map → pieDiagram-SKSYHLDU-FCGp31Lg.js.map} +1 -1
- package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-DNfJ-efQ.js → quadrantDiagram-337W2JSQ-bJcMGXM6.js} +3 -3
- package/webapp/dist/assets/{quadrantDiagram-337W2JSQ-DNfJ-efQ.js.map → quadrantDiagram-337W2JSQ-bJcMGXM6.js.map} +1 -1
- package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-Ci2ficlW.js → requirementDiagram-Z7DCOOCP-BghuL9nW.js} +4 -4
- package/webapp/dist/assets/{requirementDiagram-Z7DCOOCP-Ci2ficlW.js.map → requirementDiagram-Z7DCOOCP-BghuL9nW.js.map} +1 -1
- package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-y2RH0qmH.js → sankeyDiagram-WA2Y5GQK-DpuFpv6d.js} +2 -2
- package/webapp/dist/assets/{sankeyDiagram-WA2Y5GQK-y2RH0qmH.js.map → sankeyDiagram-WA2Y5GQK-DpuFpv6d.js.map} +1 -1
- package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-C4Mbfwyu.js → sequenceDiagram-2WXFIKYE-BM6rPANl.js} +4 -4
- package/webapp/dist/assets/{sequenceDiagram-2WXFIKYE-C4Mbfwyu.js.map → sequenceDiagram-2WXFIKYE-BM6rPANl.js.map} +1 -1
- package/webapp/dist/assets/{stateDiagram-RAJIS63D-enKQJ7WW.js → stateDiagram-RAJIS63D-C_jQSre0.js} +9 -9
- package/webapp/dist/assets/{stateDiagram-RAJIS63D-enKQJ7WW.js.map → stateDiagram-RAJIS63D-C_jQSre0.js.map} +1 -1
- package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-44zqAgYM.js → stateDiagram-v2-FVOUBMTO-BbQxj-LI.js} +5 -5
- package/webapp/dist/assets/{stateDiagram-v2-FVOUBMTO-44zqAgYM.js.map → stateDiagram-v2-FVOUBMTO-BbQxj-LI.js.map} +1 -1
- package/webapp/dist/assets/{timeline-definition-YZTLITO2-Byr_GVOH.js → timeline-definition-YZTLITO2-qVPiYzDY.js} +3 -3
- package/webapp/dist/assets/{timeline-definition-YZTLITO2-Byr_GVOH.js.map → timeline-definition-YZTLITO2-qVPiYzDY.js.map} +1 -1
- package/webapp/dist/assets/{treemap-KZPCXAKY-BSv-k7HC.js → treemap-KZPCXAKY-CH_Gjw5E.js} +5 -5
- package/webapp/dist/assets/{treemap-KZPCXAKY-BSv-k7HC.js.map → treemap-KZPCXAKY-CH_Gjw5E.js.map} +1 -1
- package/webapp/dist/assets/{vennDiagram-LZ73GAT5-CPqduRWm.js → vennDiagram-LZ73GAT5-rpOhNWvx.js} +2 -2
- package/webapp/dist/assets/{vennDiagram-LZ73GAT5-CPqduRWm.js.map → vennDiagram-LZ73GAT5-rpOhNWvx.js.map} +1 -1
- package/webapp/dist/assets/{xychartDiagram-JWTSCODW-B8yG6HaE.js → xychartDiagram-JWTSCODW-D1TcBJrI.js} +3 -3
- package/webapp/dist/assets/{xychartDiagram-JWTSCODW-B8yG6HaE.js.map → xychartDiagram-JWTSCODW-D1TcBJrI.js.map} +1 -1
- package/webapp/dist/index.html +1 -1
|
@@ -645,6 +645,7 @@ const TELLASK_SPECIAL_VIRTUAL_TOOLS = [
|
|
|
645
645
|
{
|
|
646
646
|
type: 'func',
|
|
647
647
|
name: 'replyTellask',
|
|
648
|
+
followupMode: 'deferred',
|
|
648
649
|
description: 'Deliver final reply for the current tellask session.',
|
|
649
650
|
parameters: {
|
|
650
651
|
type: 'object',
|
|
@@ -661,6 +662,7 @@ const TELLASK_SPECIAL_VIRTUAL_TOOLS = [
|
|
|
661
662
|
{
|
|
662
663
|
type: 'func',
|
|
663
664
|
name: 'replyTellaskSessionless',
|
|
665
|
+
followupMode: 'deferred',
|
|
664
666
|
description: 'Deliver final reply for the current one-shot tellask.',
|
|
665
667
|
parameters: {
|
|
666
668
|
type: 'object',
|
|
@@ -677,6 +679,7 @@ const TELLASK_SPECIAL_VIRTUAL_TOOLS = [
|
|
|
677
679
|
{
|
|
678
680
|
type: 'func',
|
|
679
681
|
name: 'replyTellaskBack',
|
|
682
|
+
followupMode: 'deferred',
|
|
680
683
|
description: 'Deliver final reply for the current tellaskBack request.',
|
|
681
684
|
parameters: {
|
|
682
685
|
type: 'object',
|
|
@@ -1228,6 +1231,49 @@ async function persistInvalidFuncCallRuntimeGuide(args) {
|
|
|
1228
1231
|
});
|
|
1229
1232
|
await persistAndPostRuntimeGuide(dlg, content);
|
|
1230
1233
|
}
|
|
1234
|
+
function isQueuedNewCourseRuntimePrompt(prompt) {
|
|
1235
|
+
return (prompt?.kind === 'new_course_runtime_guide' ||
|
|
1236
|
+
prompt?.kind === 'new_course_runtime_reply' ||
|
|
1237
|
+
prompt?.kind === 'new_course_runtime_sideDialog');
|
|
1238
|
+
}
|
|
1239
|
+
function queuedNewCourseRuntimePromptToKernelPrompt(prompt) {
|
|
1240
|
+
const common = {
|
|
1241
|
+
content: prompt.prompt,
|
|
1242
|
+
msgId: prompt.msgId,
|
|
1243
|
+
grammar: prompt.grammar ?? 'markdown',
|
|
1244
|
+
userLanguageCode: prompt.userLanguageCode,
|
|
1245
|
+
runControl: prompt.runControl,
|
|
1246
|
+
origin: 'runtime',
|
|
1247
|
+
...(prompt.skipTaskdoc === undefined ? {} : { skipTaskdoc: prompt.skipTaskdoc }),
|
|
1248
|
+
};
|
|
1249
|
+
switch (prompt.kind) {
|
|
1250
|
+
case 'new_course_runtime_guide':
|
|
1251
|
+
return common;
|
|
1252
|
+
case 'new_course_runtime_reply':
|
|
1253
|
+
return {
|
|
1254
|
+
...common,
|
|
1255
|
+
tellaskReplyDirective: prompt.tellaskReplyDirective,
|
|
1256
|
+
};
|
|
1257
|
+
case 'new_course_runtime_sideDialog':
|
|
1258
|
+
return {
|
|
1259
|
+
...common,
|
|
1260
|
+
tellaskReplyDirective: prompt.tellaskReplyDirective,
|
|
1261
|
+
calleeDialogReplyTarget: prompt.calleeDialogReplyTarget,
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
async function consumeQueuedNewCourseRuntimePromptForSameDrive(dlg) {
|
|
1266
|
+
const queuedPrompt = dlg.peekQueuedPrompt();
|
|
1267
|
+
if (!isQueuedNewCourseRuntimePrompt(queuedPrompt)) {
|
|
1268
|
+
return undefined;
|
|
1269
|
+
}
|
|
1270
|
+
const consumedPrompt = dlg.takeQueuedPrompt();
|
|
1271
|
+
if (!consumedPrompt || consumedPrompt.msgId !== queuedPrompt.msgId) {
|
|
1272
|
+
throw new Error(`queued new-course prompt invariant violation: expected queued prompt ${queuedPrompt.msgId} before same-drive continuation`);
|
|
1273
|
+
}
|
|
1274
|
+
await persistence_1.DialogPersistence.clearPendingRuntimePrompt(dlg.id, queuedPrompt.msgId, dlg.status);
|
|
1275
|
+
return queuedNewCourseRuntimePromptToKernelPrompt(queuedPrompt);
|
|
1276
|
+
}
|
|
1231
1277
|
function resolveFuncToolFollowupMode(tool) {
|
|
1232
1278
|
return tool?.followupMode ?? 'immediate';
|
|
1233
1279
|
}
|
|
@@ -1237,6 +1283,7 @@ function summarizeRoutedFunctionResult(routed) {
|
|
|
1237
1283
|
hasImmediateTellaskOutputs: routed.hasImmediateTellaskOutputs,
|
|
1238
1284
|
immediateFollowupCallIds: routed.immediateFollowupCallIds,
|
|
1239
1285
|
immediateTellaskOutputCallIds: routed.immediateTellaskOutputCallIds,
|
|
1286
|
+
invalidTellaskCallIds: routed.invalidTellaskCallIds,
|
|
1240
1287
|
shouldStopAfterReplyTool: routed.shouldStopAfterReplyTool,
|
|
1241
1288
|
shouldStopAfterPendingTellaskWait: routed.shouldStopAfterPendingTellaskWait,
|
|
1242
1289
|
pairedMessageTypes: routed.pairedMessages.map((msg) => msg.type),
|
|
@@ -1347,6 +1394,54 @@ async function maybeWriteUnexpectedIdleAfterToolRoundDebugDump(args) {
|
|
|
1347
1394
|
await promises_1.default.mkdir(debugDir, { recursive: true });
|
|
1348
1395
|
await promises_1.default.writeFile(node_path_1.default.join(debugDir, fileName), `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
|
|
1349
1396
|
}
|
|
1397
|
+
async function writeMissingImmediateFollowupTriggerDebugDump(args) {
|
|
1398
|
+
const capturedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
|
|
1399
|
+
const debugDir = node_path_1.default.resolve(process.cwd(), '.dialogs', 'debug');
|
|
1400
|
+
const trigger = args.expectation.trigger;
|
|
1401
|
+
const fileName = [
|
|
1402
|
+
'kernel-driver-missing-immediate-followup-trigger',
|
|
1403
|
+
sanitizeDebugFileSegment(capturedAt),
|
|
1404
|
+
sanitizeDebugFileSegment(args.dlg.id.rootId),
|
|
1405
|
+
sanitizeDebugFileSegment(args.dlg.id.selfId),
|
|
1406
|
+
sanitizeDebugFileSegment(trigger.triggerId),
|
|
1407
|
+
`${(0, id_1.generateShortId)()}.json`,
|
|
1408
|
+
].join('-');
|
|
1409
|
+
const activeCallees = await persistence_1.DialogPersistence.loadActiveCallees(args.dlg.id, args.dlg.status);
|
|
1410
|
+
const suspension = await args.dlg.getSuspensionStatus();
|
|
1411
|
+
const payload = {
|
|
1412
|
+
kind: args.repairOutcome === 'repaired'
|
|
1413
|
+
? 'kernel_driver_missing_immediate_followup_trigger_repaired'
|
|
1414
|
+
: 'kernel_driver_missing_immediate_followup_trigger_repair_failed',
|
|
1415
|
+
capturedAt,
|
|
1416
|
+
rtwsRootAbs: process.cwd(),
|
|
1417
|
+
repairOutcome: args.repairOutcome,
|
|
1418
|
+
checkPoint: args.checkPoint,
|
|
1419
|
+
dialog: {
|
|
1420
|
+
rootId: args.dlg.id.rootId,
|
|
1421
|
+
selfId: args.dlg.id.selfId,
|
|
1422
|
+
value: args.dlg.id.valueOf(),
|
|
1423
|
+
agentId: args.dlg.agentId,
|
|
1424
|
+
status: args.dlg.status,
|
|
1425
|
+
currentCourse: args.dlg.currentCourse,
|
|
1426
|
+
activeGenCourse: args.dlg.activeGenCourseOrUndefined ?? null,
|
|
1427
|
+
activeGenSeq: args.dlg.activeGenSeqOrUndefined ?? null,
|
|
1428
|
+
hasQueuedPrompt: args.dlg.hasQueuedPrompt(),
|
|
1429
|
+
queuedPrompt: args.dlg.peekQueuedPrompt() ?? null,
|
|
1430
|
+
},
|
|
1431
|
+
expectation: args.expectation,
|
|
1432
|
+
latestBeforeRepair: args.latestBeforeRepair,
|
|
1433
|
+
latestAfterRepair: args.latestAfterRepair,
|
|
1434
|
+
activeCallees,
|
|
1435
|
+
suspension,
|
|
1436
|
+
callstack: new Error(args.repairOutcome === 'repaired'
|
|
1437
|
+
? 'kernel-driver missing immediate followup trigger repaired'
|
|
1438
|
+
: 'kernel-driver missing immediate followup trigger repair failed').stack ?? null,
|
|
1439
|
+
};
|
|
1440
|
+
await promises_1.default.mkdir(debugDir, { recursive: true });
|
|
1441
|
+
const debugPath = node_path_1.default.join(debugDir, fileName);
|
|
1442
|
+
await promises_1.default.writeFile(debugPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
|
|
1443
|
+
return debugPath;
|
|
1444
|
+
}
|
|
1350
1445
|
async function maybeWriteIdleWithActiveReplyObligationDebugDump(args) {
|
|
1351
1446
|
if (args.finalDisplayState.kind !== 'idle_waiting_user') {
|
|
1352
1447
|
return;
|
|
@@ -1392,7 +1487,7 @@ async function maybeWriteIdleWithActiveReplyObligationDebugDump(args) {
|
|
|
1392
1487
|
await promises_1.default.mkdir(debugDir, { recursive: true });
|
|
1393
1488
|
await promises_1.default.writeFile(node_path_1.default.join(debugDir, fileName), `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
|
|
1394
1489
|
}
|
|
1395
|
-
|
|
1490
|
+
function buildImmediateFollowupTriggerExpectation(args) {
|
|
1396
1491
|
const reasons = [];
|
|
1397
1492
|
if (args.routed.immediateFollowupCallIds.length > 0) {
|
|
1398
1493
|
reasons.push({
|
|
@@ -1407,22 +1502,30 @@ async function upsertImmediateFollowupTrigger(args) {
|
|
|
1407
1502
|
replyCallId: args.routed.immediateTellaskOutputCallIds[0],
|
|
1408
1503
|
});
|
|
1409
1504
|
}
|
|
1505
|
+
const invalidRecoveryCallIds = new Set(args.routed.invalidTellaskCallIds);
|
|
1410
1506
|
if (args.invalidFuncCallCount > 0) {
|
|
1411
|
-
const
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1507
|
+
for (const call of args.streamedFuncCalls) {
|
|
1508
|
+
invalidRecoveryCallIds.add(call.id);
|
|
1509
|
+
}
|
|
1510
|
+
if (args.streamedFuncCalls.length === 0) {
|
|
1511
|
+
invalidRecoveryCallIds.add(`invalid-tool:${args.invalidFuncCallCount}`);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
if (invalidRecoveryCallIds.size > 0) {
|
|
1515
|
+
// Invalid provider tool payloads and invalid tellask specials are same-turn recovery facts,
|
|
1516
|
+
// not generic retry hints. Keep them inside the follow-up trigger so the next generation can
|
|
1517
|
+
// repair the current turn immediately, while the invalid payload itself stays loud.
|
|
1415
1518
|
reasons.push({
|
|
1416
1519
|
kind: 'invalid_tool_recovery',
|
|
1417
|
-
callIds:
|
|
1520
|
+
callIds: [...invalidRecoveryCallIds],
|
|
1418
1521
|
});
|
|
1419
1522
|
}
|
|
1420
1523
|
if (reasons.length === 0) {
|
|
1421
|
-
return;
|
|
1524
|
+
return undefined;
|
|
1422
1525
|
}
|
|
1423
1526
|
const course = args.dlg.activeGenCourseOrUndefined ?? args.dlg.currentCourse;
|
|
1424
1527
|
const genseq = args.dlg.activeGenSeq;
|
|
1425
|
-
|
|
1528
|
+
const trigger = {
|
|
1426
1529
|
triggerId: `followup:c${String(course)}:g${String(genseq)}`,
|
|
1427
1530
|
kind: 'followup',
|
|
1428
1531
|
sourceGeneration: {
|
|
@@ -1431,7 +1534,74 @@ async function upsertImmediateFollowupTrigger(args) {
|
|
|
1431
1534
|
},
|
|
1432
1535
|
reasons,
|
|
1433
1536
|
continuation: args.continuation,
|
|
1537
|
+
};
|
|
1538
|
+
return {
|
|
1539
|
+
trigger,
|
|
1540
|
+
callIds: args.streamedFuncCalls.map((call) => call.id),
|
|
1541
|
+
callNames: args.streamedFuncCalls.map((call) => call.name),
|
|
1542
|
+
routed: summarizeRoutedFunctionResult(args.routed),
|
|
1543
|
+
continuation: args.continuation,
|
|
1544
|
+
invalidFuncCallCount: args.invalidFuncCallCount,
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
async function upsertImmediateFollowupTrigger(dlg, expectation) {
|
|
1548
|
+
await persistence_1.DialogPersistence.upsertNextStepTrigger(dlg.id, expectation.trigger, dlg.status);
|
|
1549
|
+
}
|
|
1550
|
+
function hasExpectedImmediateFollowupTrigger(latest, expectation) {
|
|
1551
|
+
return (latest?.nextStep.triggers.some((trigger) => trigger.kind === 'followup' && trigger.triggerId === expectation.trigger.triggerId) === true);
|
|
1552
|
+
}
|
|
1553
|
+
async function repairMissingImmediateFollowupTrigger(args) {
|
|
1554
|
+
const expectation = args.expectation;
|
|
1555
|
+
if (expectation === undefined) {
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
const latestBeforeRepair = await persistence_1.DialogPersistence.loadDialogLatest(args.dlg.id, args.dlg.status);
|
|
1559
|
+
if (hasExpectedImmediateFollowupTrigger(latestBeforeRepair, expectation)) {
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
await persistence_1.DialogPersistence.upsertNextStepTrigger(args.dlg.id, expectation.trigger, args.dlg.status);
|
|
1563
|
+
const latestAfterRepair = await persistence_1.DialogPersistence.loadDialogLatest(args.dlg.id, args.dlg.status);
|
|
1564
|
+
const repairSucceeded = hasExpectedImmediateFollowupTrigger(latestAfterRepair, expectation);
|
|
1565
|
+
const repairOutcome = repairSucceeded
|
|
1566
|
+
? 'repaired'
|
|
1567
|
+
: 'repair_failed';
|
|
1568
|
+
const debugPath = await writeMissingImmediateFollowupTriggerDebugDump({
|
|
1569
|
+
dlg: args.dlg,
|
|
1570
|
+
expectation,
|
|
1571
|
+
latestBeforeRepair,
|
|
1572
|
+
latestAfterRepair,
|
|
1573
|
+
checkPoint: args.checkPoint,
|
|
1574
|
+
repairOutcome,
|
|
1575
|
+
});
|
|
1576
|
+
const message = `${repairSucceeded ? 'Repaired' : 'Failed to repair'} missing immediate follow-up trigger after function results ` +
|
|
1577
|
+
`(triggerId=${expectation.trigger.triggerId}, checkPoint=${args.checkPoint})`;
|
|
1578
|
+
log_1.log.error(message, new Error(`kernel_driver_missing_immediate_followup_trigger_${repairOutcome}`), {
|
|
1579
|
+
rootId: args.dlg.id.rootId,
|
|
1580
|
+
selfId: args.dlg.id.selfId,
|
|
1581
|
+
dialogId: args.dlg.id.valueOf(),
|
|
1582
|
+
status: args.dlg.status,
|
|
1583
|
+
triggerId: expectation.trigger.triggerId,
|
|
1584
|
+
checkPoint: args.checkPoint,
|
|
1585
|
+
repairSucceeded,
|
|
1586
|
+
sourceGeneration: expectation.trigger.sourceGeneration,
|
|
1587
|
+
reasonKinds: expectation.trigger.reasons.map((reason) => reason.kind),
|
|
1588
|
+
callIds: expectation.callIds,
|
|
1589
|
+
callNames: expectation.callNames,
|
|
1590
|
+
invalidFuncCallCount: expectation.invalidFuncCallCount,
|
|
1591
|
+
continuation: expectation.continuation,
|
|
1592
|
+
routed: expectation.routed,
|
|
1593
|
+
latestBeforeRepairNextStep: latestBeforeRepair?.nextStep ?? null,
|
|
1594
|
+
latestBeforeRepairGenerationRunState: latestBeforeRepair?.generationRunState ?? null,
|
|
1595
|
+
latestBeforeRepairDisplayState: latestBeforeRepair?.displayState ?? null,
|
|
1596
|
+
latestAfterRepairNextStep: latestAfterRepair?.nextStep ?? null,
|
|
1597
|
+
latestAfterRepairGenerationRunState: latestAfterRepair?.generationRunState ?? null,
|
|
1598
|
+
latestAfterRepairDisplayState: latestAfterRepair?.displayState ?? null,
|
|
1599
|
+
debugPath,
|
|
1434
1600
|
});
|
|
1601
|
+
await args.dlg.streamError(`${message}; debug=${debugPath}`);
|
|
1602
|
+
if (!repairSucceeded) {
|
|
1603
|
+
throw new Error(`${message}; debug=${debugPath}`);
|
|
1604
|
+
}
|
|
1435
1605
|
}
|
|
1436
1606
|
function shouldImmediatelyFollowUpSuccessfulToolResult(tool) {
|
|
1437
1607
|
return resolveFuncToolFollowupMode(tool) === 'immediate';
|
|
@@ -1708,6 +1878,7 @@ async function executeFunctionRound(args) {
|
|
|
1708
1878
|
hasImmediateTellaskOutputs: false,
|
|
1709
1879
|
immediateFollowupCallIds: [],
|
|
1710
1880
|
immediateTellaskOutputCallIds: [],
|
|
1881
|
+
invalidTellaskCallIds: [],
|
|
1711
1882
|
shouldStopAfterReplyTool: false,
|
|
1712
1883
|
shouldStopAfterPendingTellaskWait: false,
|
|
1713
1884
|
pairedMessages: [],
|
|
@@ -1818,6 +1989,7 @@ async function executeFunctionRound(args) {
|
|
|
1818
1989
|
hasImmediateTellaskOutputs: tellaskRound.hasImmediateTellaskOutputs,
|
|
1819
1990
|
immediateFollowupCallIds,
|
|
1820
1991
|
immediateTellaskOutputCallIds: tellaskRound.immediateTellaskOutputCallIds,
|
|
1992
|
+
invalidTellaskCallIds: tellaskRound.invalidTellaskCallIds,
|
|
1821
1993
|
shouldStopAfterReplyTool: tellaskRound.shouldStopAfterReplyTool,
|
|
1822
1994
|
shouldStopAfterPendingTellaskWait: tellaskRound.shouldStopAfterPendingTellaskWait,
|
|
1823
1995
|
pairedMessages,
|
|
@@ -2269,6 +2441,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
|
|
|
2269
2441
|
}
|
|
2270
2442
|
lastBusinessContinuation = currentBusinessContinuation;
|
|
2271
2443
|
let generationBodyError;
|
|
2444
|
+
let immediateFollowupTriggerExpectation;
|
|
2272
2445
|
const q4hAnswerCallId = normalizeQ4HAnswerCallId(currentPrompt?.q4hAnswerCallId);
|
|
2273
2446
|
const isQ4HAnswerPrompt = q4hAnswerCallId !== undefined;
|
|
2274
2447
|
try {
|
|
@@ -3139,13 +3312,44 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
|
|
|
3139
3312
|
});
|
|
3140
3313
|
break;
|
|
3141
3314
|
}
|
|
3315
|
+
const queuedNewCoursePrompt = await consumeQueuedNewCourseRuntimePromptForSameDrive(dlg);
|
|
3316
|
+
if (queuedNewCoursePrompt !== undefined) {
|
|
3317
|
+
pendingPrompt = queuedNewCoursePrompt;
|
|
3318
|
+
skipTaskdocForThisDrive = false;
|
|
3319
|
+
continue;
|
|
3320
|
+
}
|
|
3321
|
+
// Start an immediate post-tool generation only when this round produced tool outputs that
|
|
3322
|
+
// warrant same-drive LLM reaction right away. Provider-native side-channel UI events are
|
|
3323
|
+
// meaningful output, but they are not transcript/context inputs and therefore must not
|
|
3324
|
+
// trigger another immediate generation round by themselves.
|
|
3325
|
+
const shouldStartImmediatePostToolGeneration = routed.hasImmediateFollowupToolCalls ||
|
|
3326
|
+
routed.hasImmediateTellaskOutputs ||
|
|
3327
|
+
invalidFuncCallCount > 0;
|
|
3328
|
+
if (shouldStartImmediatePostToolGeneration) {
|
|
3329
|
+
const expectation = buildImmediateFollowupTriggerExpectation({
|
|
3330
|
+
dlg,
|
|
3331
|
+
routed,
|
|
3332
|
+
invalidFuncCallCount,
|
|
3333
|
+
streamedFuncCalls,
|
|
3334
|
+
continuation: currentBusinessContinuation,
|
|
3335
|
+
});
|
|
3336
|
+
if (expectation === undefined) {
|
|
3337
|
+
throw new Error(`Immediate follow-up trigger invariant violation: expected trigger reasons missing ` +
|
|
3338
|
+
`(dialog=${dlg.id.valueOf()}, course=${String(dlg.activeGenCourseOrUndefined ?? dlg.currentCourse)}, ` +
|
|
3339
|
+
`genseq=${String(dlg.activeGenSeq)}, immediateToolCalls=${String(routed.hasImmediateFollowupToolCalls)}, ` +
|
|
3340
|
+
`immediateTellaskOutputs=${String(routed.hasImmediateTellaskOutputs)}, ` +
|
|
3341
|
+
`invalidFuncCallCount=${String(invalidFuncCallCount)})`);
|
|
3342
|
+
}
|
|
3343
|
+
immediateFollowupTriggerExpectation = expectation;
|
|
3344
|
+
await upsertImmediateFollowupTrigger(dlg, immediateFollowupTriggerExpectation);
|
|
3345
|
+
}
|
|
3142
3346
|
if (dlg.hasQueuedPrompt()) {
|
|
3143
3347
|
lastToolRoundStopDiagnostics = buildToolRoundStopDiagnostics({
|
|
3144
3348
|
dlg,
|
|
3145
3349
|
streamedFuncCalls,
|
|
3146
3350
|
routed,
|
|
3147
3351
|
lastBusinessContinuation,
|
|
3148
|
-
shouldStartImmediatePostToolGeneration
|
|
3352
|
+
shouldStartImmediatePostToolGeneration,
|
|
3149
3353
|
stopReason: 'queued_prompt_after_tool_round',
|
|
3150
3354
|
queuedPromptAfterToolRound: true,
|
|
3151
3355
|
remindersVer: dlg.remindersVer,
|
|
@@ -3166,7 +3370,7 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
|
|
|
3166
3370
|
streamedFuncCalls,
|
|
3167
3371
|
routed,
|
|
3168
3372
|
lastBusinessContinuation,
|
|
3169
|
-
shouldStartImmediatePostToolGeneration
|
|
3373
|
+
shouldStartImmediatePostToolGeneration,
|
|
3170
3374
|
stopReason: 'suspended_after_tool_round',
|
|
3171
3375
|
suspensionAfterToolRound,
|
|
3172
3376
|
queuedPromptAfterToolRound: dlg.hasQueuedPrompt(),
|
|
@@ -3176,13 +3380,6 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
|
|
|
3176
3380
|
await preserveDiligenceBudgetAcrossQ4H(dlg);
|
|
3177
3381
|
break;
|
|
3178
3382
|
}
|
|
3179
|
-
// Start an immediate post-tool generation only when this round produced tool outputs that
|
|
3180
|
-
// warrant same-drive LLM reaction right away. Provider-native side-channel UI events are
|
|
3181
|
-
// meaningful output, but they are not transcript/context inputs and therefore must not
|
|
3182
|
-
// trigger another immediate generation round by themselves.
|
|
3183
|
-
const shouldStartImmediatePostToolGeneration = routed.hasImmediateFollowupToolCalls ||
|
|
3184
|
-
routed.hasImmediateTellaskOutputs ||
|
|
3185
|
-
invalidFuncCallCount > 0;
|
|
3186
3383
|
if (!shouldStartImmediatePostToolGeneration) {
|
|
3187
3384
|
if (routed.shouldStopAfterPendingTellaskWait) {
|
|
3188
3385
|
lastToolRoundStopDiagnostics = buildToolRoundStopDiagnostics({
|
|
@@ -3239,23 +3436,19 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
|
|
|
3239
3436
|
});
|
|
3240
3437
|
break;
|
|
3241
3438
|
}
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
? currentUserPromptMsgId
|
|
3256
|
-
: undefined;
|
|
3257
|
-
continue;
|
|
3258
|
-
}
|
|
3439
|
+
await repairMissingImmediateFollowupTrigger({
|
|
3440
|
+
dlg,
|
|
3441
|
+
expectation: immediateFollowupTriggerExpectation,
|
|
3442
|
+
checkPoint: 'before_immediate_post_tool_generation_continue',
|
|
3443
|
+
});
|
|
3444
|
+
resolvingImmediateToolResultForUserPrompt =
|
|
3445
|
+
currentGenerationBelongsToUserPrompt ||
|
|
3446
|
+
currentGenerationBelongsToUserToolChain ||
|
|
3447
|
+
isUserOriginPrompt(currentPrompt);
|
|
3448
|
+
resolvingImmediateToolResultUserPromptMsgId = resolvingImmediateToolResultForUserPrompt
|
|
3449
|
+
? currentUserPromptMsgId
|
|
3450
|
+
: undefined;
|
|
3451
|
+
continue;
|
|
3259
3452
|
}
|
|
3260
3453
|
catch (err) {
|
|
3261
3454
|
generationBodyError = err;
|
|
@@ -3263,6 +3456,13 @@ async function driveDialogStreamCore(dlg, callbacks, humanPrompt, driveOptions)
|
|
|
3263
3456
|
}
|
|
3264
3457
|
finally {
|
|
3265
3458
|
try {
|
|
3459
|
+
if (generationBodyError === undefined) {
|
|
3460
|
+
await repairMissingImmediateFollowupTrigger({
|
|
3461
|
+
dlg,
|
|
3462
|
+
expectation: immediateFollowupTriggerExpectation,
|
|
3463
|
+
checkPoint: 'before_notify_generating_finish',
|
|
3464
|
+
});
|
|
3465
|
+
}
|
|
3266
3466
|
await dlg.notifyGeneratingFinish(contextHealthForGen, llmGenModelForGen);
|
|
3267
3467
|
}
|
|
3268
3468
|
catch (finishErr) {
|
|
@@ -48,6 +48,7 @@ function buildFbrConclusionTools(language) {
|
|
|
48
48
|
{
|
|
49
49
|
type: 'func',
|
|
50
50
|
name: exports.FBR_LOW_NOISE_CONCLUSION_TOOL_NAME,
|
|
51
|
+
followupMode: 'deferred',
|
|
51
52
|
description: buildFbrConclusionToolDescription({ language, kind: 'low_noise' }),
|
|
52
53
|
parameters: {
|
|
53
54
|
type: 'object',
|
|
@@ -67,6 +68,7 @@ function buildFbrConclusionTools(language) {
|
|
|
67
68
|
{
|
|
68
69
|
type: 'func',
|
|
69
70
|
name: exports.FBR_UNREASONABLE_SITUATION_TOOL_NAME,
|
|
71
|
+
followupMode: 'deferred',
|
|
70
72
|
description: buildFbrConclusionToolDescription({ language, kind: 'unreasonable' }),
|
|
71
73
|
parameters: {
|
|
72
74
|
type: 'object',
|
|
@@ -121,6 +121,7 @@ export type TellaskFunctionRoundResult = Readonly<{
|
|
|
121
121
|
hasInvalidTellaskCalls: boolean;
|
|
122
122
|
hasImmediateTellaskOutputs: boolean;
|
|
123
123
|
immediateTellaskOutputCallIds: readonly string[];
|
|
124
|
+
invalidTellaskCallIds: readonly string[];
|
|
124
125
|
shouldStopAfterReplyTool: boolean;
|
|
125
126
|
shouldStopAfterPendingTellaskWait: boolean;
|
|
126
127
|
}>;
|
|
@@ -2396,6 +2396,10 @@ async function processTellaskFunctionRound(args) {
|
|
|
2396
2396
|
const result = output;
|
|
2397
2397
|
tellaskFuncResultByCallId.set(result.id, result);
|
|
2398
2398
|
tellaskFuncResults.push(result);
|
|
2399
|
+
const originatingCall = specialCallById.get(result.id);
|
|
2400
|
+
if (originatingCall !== undefined && isReplyTellaskCallName(originatingCall.callName)) {
|
|
2401
|
+
continue;
|
|
2402
|
+
}
|
|
2399
2403
|
hasImmediateTellaskOutputs = true;
|
|
2400
2404
|
immediateTellaskOutputCallIds.push(result.id);
|
|
2401
2405
|
continue;
|
|
@@ -2460,7 +2464,8 @@ async function processTellaskFunctionRound(args) {
|
|
|
2460
2464
|
hasInvalidTellaskCalls: orderedInvalidCalls.length > 0,
|
|
2461
2465
|
hasImmediateTellaskOutputs,
|
|
2462
2466
|
immediateTellaskOutputCallIds,
|
|
2463
|
-
|
|
2467
|
+
invalidTellaskCallIds: orderedInvalidCalls.map((issue) => issue.originalCall.id),
|
|
2468
|
+
shouldStopAfterReplyTool: tellaskExecution.successfulReplyCallIds.length > 0,
|
|
2464
2469
|
shouldStopAfterPendingTellaskWait,
|
|
2465
2470
|
};
|
|
2466
2471
|
}
|
|
@@ -5,6 +5,7 @@ export declare function configureDomindsSelfUpdate(params: {
|
|
|
5
5
|
host: string;
|
|
6
6
|
port: number;
|
|
7
7
|
mode: ServerMode;
|
|
8
|
+
closeWebSocketClients: () => void;
|
|
8
9
|
stopServer: () => Promise<void>;
|
|
9
10
|
}): void;
|
|
10
11
|
export declare function setDomindsSelfUpdateBroadcaster(next: ((status: DomindsSelfUpdateStatus) => void) | null): void;
|
|
@@ -20,6 +20,8 @@ const BACKGROUND_CHECK_INTERVAL_MS = 30 * 60 * 1000;
|
|
|
20
20
|
const LATEST_VERSION_CHECK_TIMEOUT_MS = 60000;
|
|
21
21
|
const RESTART_PORT_RELEASE_TIMEOUT_MS = 15000;
|
|
22
22
|
const RESTART_PORT_PROBE_INTERVAL_MS = 150;
|
|
23
|
+
const RESTART_EXIT_GRACE_MS = 1000;
|
|
24
|
+
const RESTART_FORCE_KILL_AFTER_MS = 3000;
|
|
23
25
|
const COMMAND_OUTPUT_LOG_LIMIT = 2000;
|
|
24
26
|
const PROXY_URL_ENV_KEYS = new Set([
|
|
25
27
|
'HTTP_PROXY',
|
|
@@ -97,6 +99,9 @@ function truncateCommandOutput(value) {
|
|
|
97
99
|
return raw;
|
|
98
100
|
return `${raw.slice(0, COMMAND_OUTPUT_LOG_LIMIT)}...[truncated ${raw.length - COMMAND_OUTPUT_LOG_LIMIT} chars]`;
|
|
99
101
|
}
|
|
102
|
+
function delayMs(ms) {
|
|
103
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
104
|
+
}
|
|
100
105
|
function formatPathEnvExcerpt(pathEnv) {
|
|
101
106
|
if (pathEnv === null || pathEnv.trim() === '')
|
|
102
107
|
return null;
|
|
@@ -620,6 +625,7 @@ function configureDomindsSelfUpdate(params) {
|
|
|
620
625
|
host: params.host,
|
|
621
626
|
port: params.port,
|
|
622
627
|
mode: params.mode,
|
|
628
|
+
closeWebSocketClients: params.closeWebSocketClients,
|
|
623
629
|
stopServer: params.stopServer,
|
|
624
630
|
};
|
|
625
631
|
latestObservation = { kind: 'unknown' };
|
|
@@ -860,6 +866,8 @@ function spawnDetachedRestartHelper(params) {
|
|
|
860
866
|
cwd: params.cwd,
|
|
861
867
|
host: getRestartPortProbeHost(params.host),
|
|
862
868
|
port: params.port,
|
|
869
|
+
retiringPid: process.pid,
|
|
870
|
+
forceKillAfterMs: RESTART_FORCE_KILL_AFTER_MS,
|
|
863
871
|
probeIntervalMs: RESTART_PORT_PROBE_INTERVAL_MS,
|
|
864
872
|
portReleaseTimeoutMs: RESTART_PORT_RELEASE_TIMEOUT_MS,
|
|
865
873
|
stdioMode,
|
|
@@ -884,8 +892,8 @@ function spawnDetachedRestartHelper(params) {
|
|
|
884
892
|
' socket.setTimeout(1000, () => finish(true));',
|
|
885
893
|
' });',
|
|
886
894
|
'}',
|
|
887
|
-
'async function waitForPortRelease() {',
|
|
888
|
-
' const deadline = Date.now() +
|
|
895
|
+
'async function waitForPortRelease(timeoutMs) {',
|
|
896
|
+
' const deadline = Date.now() + timeoutMs;',
|
|
889
897
|
' let consecutiveIdle = 0;',
|
|
890
898
|
' while (Date.now() < deadline) {',
|
|
891
899
|
' if (await isPortBusy()) {',
|
|
@@ -894,13 +902,42 @@ function spawnDetachedRestartHelper(params) {
|
|
|
894
902
|
' continue;',
|
|
895
903
|
' }',
|
|
896
904
|
' consecutiveIdle += 1;',
|
|
897
|
-
' if (consecutiveIdle >= 2) return;',
|
|
905
|
+
' if (consecutiveIdle >= 2) return true;',
|
|
898
906
|
' await new Promise((resolve) => setTimeout(resolve, payload.probeIntervalMs));',
|
|
899
907
|
' }',
|
|
908
|
+
' return false;',
|
|
909
|
+
'}',
|
|
910
|
+
'function forceKillRetiringProcess() {',
|
|
911
|
+
' if (!Number.isInteger(payload.retiringPid) || payload.retiringPid <= 0) {',
|
|
912
|
+
' throw new Error(`Invalid retiring Dominds pid for restart: ${String(payload.retiringPid)}`);',
|
|
913
|
+
' }',
|
|
914
|
+
' if (payload.retiringPid === process.pid) {',
|
|
915
|
+
' throw new Error(`Refusing to kill restart helper pid ${String(process.pid)}`);',
|
|
916
|
+
' }',
|
|
917
|
+
" const killer = process.platform === 'win32'",
|
|
918
|
+
" ? spawn('taskkill.exe', ['/PID', String(payload.retiringPid), '/F'], { stdio: payload.stdioMode })",
|
|
919
|
+
" : spawn('kill', ['-KILL', String(payload.retiringPid)], { stdio: payload.stdioMode });",
|
|
920
|
+
' return new Promise((resolve, reject) => {',
|
|
921
|
+
" killer.once('error', reject);",
|
|
922
|
+
" killer.once('exit', (code) => {",
|
|
923
|
+
' if (code === 0) {',
|
|
924
|
+
' resolve();',
|
|
925
|
+
' return;',
|
|
926
|
+
' }',
|
|
927
|
+
' resolve();',
|
|
928
|
+
' });',
|
|
929
|
+
' });',
|
|
900
930
|
'}',
|
|
901
931
|
'(async () => {',
|
|
902
932
|
' try {',
|
|
903
|
-
' await waitForPortRelease();',
|
|
933
|
+
' const releasedGracefully = await waitForPortRelease(payload.forceKillAfterMs);',
|
|
934
|
+
' if (!releasedGracefully) {',
|
|
935
|
+
' await forceKillRetiringProcess();',
|
|
936
|
+
' const releasedAfterKill = await waitForPortRelease(payload.portReleaseTimeoutMs);',
|
|
937
|
+
' if (!releasedAfterKill) {',
|
|
938
|
+
' throw new Error(`Dominds restart port ${String(payload.host)}:${String(payload.port)} is still busy after force-killing pid ${String(payload.retiringPid)}`);',
|
|
939
|
+
' }',
|
|
940
|
+
' }',
|
|
904
941
|
" const child = spawn(payload.command, payload.args, { cwd: payload.cwd, env: process.env, detached, stdio: payload.stdioMode, shell: process.platform === 'win32' });",
|
|
905
942
|
' if (detached) child.unref();',
|
|
906
943
|
' process.exit(0);',
|
|
@@ -922,7 +959,28 @@ function spawnDetachedRestartHelper(params) {
|
|
|
922
959
|
}
|
|
923
960
|
async function stopAndExitForRestart() {
|
|
924
961
|
const cfg = assertRuntimeConfig();
|
|
925
|
-
|
|
962
|
+
let stopSettled = false;
|
|
963
|
+
void cfg
|
|
964
|
+
.stopServer()
|
|
965
|
+
.then(() => {
|
|
966
|
+
stopSettled = true;
|
|
967
|
+
})
|
|
968
|
+
.catch((error) => {
|
|
969
|
+
stopSettled = true;
|
|
970
|
+
log.error('Failed to stop Dominds HTTP server during restart grace window', error, {
|
|
971
|
+
host: cfg.host,
|
|
972
|
+
port: cfg.port,
|
|
973
|
+
});
|
|
974
|
+
});
|
|
975
|
+
cfg.closeWebSocketClients();
|
|
976
|
+
await delayMs(RESTART_EXIT_GRACE_MS);
|
|
977
|
+
if (!stopSettled) {
|
|
978
|
+
log.warn('Exiting Dominds process before graceful HTTP server stop completed during restart', undefined, {
|
|
979
|
+
host: cfg.host,
|
|
980
|
+
port: cfg.port,
|
|
981
|
+
graceMs: RESTART_EXIT_GRACE_MS,
|
|
982
|
+
});
|
|
983
|
+
}
|
|
926
984
|
process.exit(0);
|
|
927
985
|
}
|
|
928
986
|
async function restartDomindsIntoLatest() {
|
|
@@ -1035,8 +1035,23 @@ async function handleDisplayDialog(ws, packet) {
|
|
|
1035
1035
|
}
|
|
1036
1036
|
const rootPrimingConfig = dialogIdObj.selfId === dialogIdObj.rootId ? (0, priming_1.getMainDialogPrimingConfig)(metadata) : undefined;
|
|
1037
1037
|
const showPrimingEventsInUi = rootPrimingConfig?.showInUi !== false;
|
|
1038
|
-
|
|
1038
|
+
if (packet.course !== undefined && (!Number.isInteger(packet.course) || packet.course <= 0)) {
|
|
1039
|
+
ws.send(JSON.stringify({
|
|
1040
|
+
type: 'error',
|
|
1041
|
+
message: 'display_dialog course must be a positive integer when provided',
|
|
1042
|
+
}));
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
const latestCourse = (await persistence_1.DialogPersistence.getCurrentCourseNumber(dialogIdObj, requestedStatus)) ||
|
|
1039
1046
|
(dialogState.currentCourse ?? 1);
|
|
1047
|
+
const decidedCourse = packet.course ?? latestCourse;
|
|
1048
|
+
if (decidedCourse > latestCourse) {
|
|
1049
|
+
ws.send(JSON.stringify({
|
|
1050
|
+
type: 'error',
|
|
1051
|
+
message: `display_dialog course ${String(decidedCourse)} exceeds latest course ${String(latestCourse)}`,
|
|
1052
|
+
}));
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1040
1055
|
const enableLive = requestedStatus === 'running';
|
|
1041
1056
|
const mainDialog = await (0, dialog_instance_registry_1.getOrRestoreMainDialog)(dialogIdObj.rootId, requestedStatus);
|
|
1042
1057
|
if (!mainDialog) {
|
|
@@ -1068,7 +1083,7 @@ async function handleDisplayDialog(ws, packet) {
|
|
|
1068
1083
|
try {
|
|
1069
1084
|
const dialogStore = dialog.dlgStore;
|
|
1070
1085
|
if (dialogStore instanceof persistence_1.DiskFileDialogStore) {
|
|
1071
|
-
await dialogStore.sendDialogEventsDirectly(ws, dialog, decidedCourse,
|
|
1086
|
+
await dialogStore.sendDialogEventsDirectly(ws, dialog, decidedCourse, latestCourse, requestedStatus, { showPrimingEventsInUi });
|
|
1072
1087
|
}
|
|
1073
1088
|
else {
|
|
1074
1089
|
throw new Error('Unexpected dialog store type for sendDialogEventsDirectly');
|
|
@@ -1340,6 +1355,13 @@ async function handleDisplayCourse(ws, packet) {
|
|
|
1340
1355
|
if (!dialog || typeof course !== 'number') {
|
|
1341
1356
|
throw new Error('dialog and course are required');
|
|
1342
1357
|
}
|
|
1358
|
+
if (!Number.isInteger(course) || course <= 0) {
|
|
1359
|
+
ws.send(JSON.stringify({
|
|
1360
|
+
type: 'error',
|
|
1361
|
+
message: 'display_course course must be a positive integer',
|
|
1362
|
+
}));
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1343
1365
|
// Extract dialog ID from DialogIdent
|
|
1344
1366
|
let dialogIdStr = dialog.selfId;
|
|
1345
1367
|
let mainDialogIdStr = dialog.rootId;
|
|
@@ -1371,6 +1393,13 @@ async function handleDisplayCourse(ws, packet) {
|
|
|
1371
1393
|
return;
|
|
1372
1394
|
}
|
|
1373
1395
|
const totalCourses = (await persistence_1.DialogPersistence.getCurrentCourseNumber(dialogId, requestedStatus)) || course;
|
|
1396
|
+
if (course > totalCourses) {
|
|
1397
|
+
ws.send(JSON.stringify({
|
|
1398
|
+
type: 'error',
|
|
1399
|
+
message: `display_course course ${String(course)} exceeds latest course ${String(totalCourses)}`,
|
|
1400
|
+
}));
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1374
1403
|
const mainDialog = await (0, dialog_instance_registry_1.getOrRestoreMainDialog)(dialogId.rootId, requestedStatus);
|
|
1375
1404
|
if (!mainDialog)
|
|
1376
1405
|
return;
|
package/dist/server.js
CHANGED
|
@@ -233,6 +233,19 @@ async function startServer(opts = {}) {
|
|
|
233
233
|
host,
|
|
234
234
|
port: boundPort,
|
|
235
235
|
mode: serverMode,
|
|
236
|
+
closeWebSocketClients: () => {
|
|
237
|
+
if (clients.size === 0)
|
|
238
|
+
return;
|
|
239
|
+
log.info(`Closing ${clients.size} WebSocket client(s) for Dominds restart`);
|
|
240
|
+
for (const ws of clients) {
|
|
241
|
+
try {
|
|
242
|
+
ws.close(1012, 'server_restart');
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
log.warn('Failed to close WebSocket client for Dominds restart', error);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
},
|
|
236
249
|
stopServer: async () => {
|
|
237
250
|
await httpServer.stop();
|
|
238
251
|
},
|
package/dist/tools/ctrl.js
CHANGED
|
@@ -612,6 +612,7 @@ exports.updateReminderTool = {
|
|
|
612
612
|
exports.clearMindTool = {
|
|
613
613
|
type: 'func',
|
|
614
614
|
name: 'clear_mind',
|
|
615
|
+
followupMode: 'deferred',
|
|
615
616
|
description: 'Start a new dialog course, optionally carrying one extra continuation reminder.',
|
|
616
617
|
descriptionI18n: {
|
|
617
618
|
en: 'Start a new dialog course, optionally carrying one extra continuation reminder.',
|