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.
Files changed (47) hide show
  1. package/dist/apps/runtime.js +3 -1
  2. package/dist/dialog-fork.js +2 -1
  3. package/dist/dialog-global-registry.d.ts +11 -1
  4. package/dist/dialog-global-registry.js +45 -0
  5. package/dist/dialog.d.ts +14 -5
  6. package/dist/dialog.js +114 -21
  7. package/dist/docs/daemon-cmd-runner.md +5 -0
  8. package/dist/docs/daemon-cmd-runner.zh.md +5 -0
  9. package/dist/llm/kernel-driver/drive.js +228 -49
  10. package/dist/llm/kernel-driver/fbr.d.ts +9 -0
  11. package/dist/llm/kernel-driver/fbr.js +186 -59
  12. package/dist/mcp/supervisor.js +4 -1
  13. package/dist/minds/load.js +1 -0
  14. package/dist/minds/system-prompt-parts.js +30 -30
  15. package/dist/persistence.js +83 -17
  16. package/dist/priming.js +2 -3
  17. package/dist/runtime/driver-messages.d.ts +9 -0
  18. package/dist/runtime/driver-messages.js +103 -33
  19. package/dist/runtime/shared-reminder-update-impact.d.ts +20 -0
  20. package/dist/runtime/shared-reminder-update-impact.js +110 -0
  21. package/dist/shared-reminders.js +2 -2
  22. package/dist/tool-availability.js +1 -0
  23. package/dist/tool.d.ts +7 -4
  24. package/dist/tool.js +10 -4
  25. package/dist/tools/app-reminders.js +4 -3
  26. package/dist/tools/builtins.js +2 -0
  27. package/dist/tools/cmd-runner-protocol.d.ts +6 -0
  28. package/dist/tools/cmd-runner-protocol.js +57 -2
  29. package/dist/tools/cmd-runner.js +83 -2
  30. package/dist/tools/ctrl.d.ts +2 -0
  31. package/dist/tools/ctrl.js +183 -6
  32. package/dist/tools/os.js +115 -14
  33. package/dist/tools/pending-tellask-reminder.js +1 -0
  34. package/dist/tools/process-kill.js +49 -0
  35. package/dist/tools/prompts/control/en/errors.md +1 -1
  36. package/dist/tools/prompts/control/en/index.md +4 -4
  37. package/dist/tools/prompts/control/en/principles.md +22 -21
  38. package/dist/tools/prompts/control/en/scenarios.md +10 -3
  39. package/dist/tools/prompts/control/en/tools.md +28 -5
  40. package/dist/tools/prompts/control/zh/errors.md +1 -1
  41. package/dist/tools/prompts/control/zh/index.md +4 -4
  42. package/dist/tools/prompts/control/zh/principles.md +21 -20
  43. package/dist/tools/prompts/control/zh/scenarios.md +10 -3
  44. package/dist/tools/prompts/control/zh/tools.md +28 -5
  45. package/dist/tools/prompts/os/en/tools.md +2 -0
  46. package/dist/tools/prompts/os/zh/tools.md +2 -0
  47. package/package.json +4 -4
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dispatchSharedReminderUpdateImpact = dispatchSharedReminderUpdateImpact;
4
+ exports.dispatchSharedReminderMigrationImpact = dispatchSharedReminderMigrationImpact;
5
+ const id_1 = require("@longrun-ai/kernel/utils/id");
6
+ const dialog_global_registry_1 = require("../dialog-global-registry");
7
+ const driver_messages_1 = require("./driver-messages");
8
+ function sharedReminderUpdateImpactsDialog(args) {
9
+ if (args.candidate.id.equals(args.updater.id)) {
10
+ return false;
11
+ }
12
+ if (args.candidate.agentId !== args.updater.agentId) {
13
+ return false;
14
+ }
15
+ return args.scope === 'task' ? args.candidate.taskDocPath === args.updater.taskDocPath : true;
16
+ }
17
+ function collectSharedReminderUpdatePeerDialogs(args) {
18
+ const peers = [];
19
+ for (const candidate of dialog_global_registry_1.globalDialogRegistry.getLoadedInFlightDialogsForSharedReminderImpact()) {
20
+ if (sharedReminderUpdateImpactsDialog({
21
+ scope: args.scope,
22
+ updater: args.updater,
23
+ candidate,
24
+ })) {
25
+ peers.push(candidate);
26
+ }
27
+ }
28
+ return peers;
29
+ }
30
+ async function queueSharedReminderUpdateImpactPrompt(args) {
31
+ await args.targetDialog.queueRuntimeGuidePrompt({
32
+ prompt: (0, driver_messages_1.formatSharedReminderUpdateImpactNotice)(args.language, {
33
+ reminderId: args.reminderId,
34
+ scope: args.scope,
35
+ audience: 'peer',
36
+ }),
37
+ msgId: (0, id_1.generateShortId)(),
38
+ grammar: 'markdown',
39
+ userLanguageCode: args.language,
40
+ skipTaskdoc: true,
41
+ });
42
+ dialog_global_registry_1.globalDialogRegistry.queueRootDrive(args.targetDialog.id.rootId, {
43
+ source: 'shared_reminder_update_impact',
44
+ reason: `reminder_id:${args.reminderId}`,
45
+ });
46
+ }
47
+ async function queueSharedReminderMigrationImpactPrompt(args) {
48
+ await args.targetDialog.queueRuntimeGuidePrompt({
49
+ prompt: (0, driver_messages_1.formatSharedReminderMigrationImpactNotice)(args.language, {
50
+ reminderId: args.reminderId,
51
+ scope: args.scope,
52
+ }),
53
+ msgId: (0, id_1.generateShortId)(),
54
+ grammar: 'markdown',
55
+ userLanguageCode: args.language,
56
+ skipTaskdoc: true,
57
+ });
58
+ dialog_global_registry_1.globalDialogRegistry.queueRootDrive(args.targetDialog.id.rootId, {
59
+ source: 'shared_reminder_migration_impact',
60
+ reason: `migrated_reminder_id:${args.reminderId}`,
61
+ });
62
+ }
63
+ async function dispatchSharedReminderUpdateImpact(args) {
64
+ const peerDialogs = collectSharedReminderUpdatePeerDialogs({
65
+ updater: args.updater,
66
+ scope: args.scope,
67
+ });
68
+ if (peerDialogs.length === 0) {
69
+ return undefined;
70
+ }
71
+ let dispatchedDialogCount = 0;
72
+ for (const peerDialog of peerDialogs) {
73
+ await queueSharedReminderUpdateImpactPrompt({
74
+ targetDialog: peerDialog,
75
+ reminderId: args.reminderId,
76
+ scope: args.scope,
77
+ language: args.language,
78
+ });
79
+ dispatchedDialogCount += 1;
80
+ }
81
+ return {
82
+ scope: args.scope,
83
+ peerDialogCount: peerDialogs.length,
84
+ dispatchedDialogCount,
85
+ };
86
+ }
87
+ async function dispatchSharedReminderMigrationImpact(args) {
88
+ const peerDialogs = collectSharedReminderUpdatePeerDialogs({
89
+ updater: args.updater,
90
+ scope: args.scope,
91
+ });
92
+ if (peerDialogs.length === 0) {
93
+ return undefined;
94
+ }
95
+ let dispatchedDialogCount = 0;
96
+ for (const peerDialog of peerDialogs) {
97
+ await queueSharedReminderMigrationImpactPrompt({
98
+ targetDialog: peerDialog,
99
+ reminderId: args.reminderId,
100
+ scope: args.scope,
101
+ language: args.language,
102
+ });
103
+ dispatchedDialogCount += 1;
104
+ }
105
+ return {
106
+ scope: args.scope,
107
+ peerDialogCount: peerDialogs.length,
108
+ dispatchedDialogCount,
109
+ };
110
+ }
@@ -106,8 +106,8 @@ function serializeReminder(reminder) {
106
106
  ownerName: reminder.owner?.name,
107
107
  meta: reminder.meta,
108
108
  echoback: reminder.echoback,
109
- scope: reminder.scope ?? 'agent',
110
- renderMode: reminder.renderMode ?? 'markdown',
109
+ scope: reminder.scope,
110
+ renderMode: reminder.renderMode,
111
111
  createdAt: reminder.createdAt ?? (0, time_1.formatUnifiedTimestamp)(new Date()),
112
112
  priority: reminder.priority ?? 'medium',
113
113
  };
@@ -225,6 +225,7 @@ function buildIntrinsicControlToolset(dialog) {
225
225
  ctrl_1.addReminderTool,
226
226
  ctrl_1.deleteReminderTool,
227
227
  ctrl_1.updateReminderTool,
228
+ ctrl_1.migrateReminderTool,
228
229
  ctrl_1.clearMindTool,
229
230
  ctrl_1.recallTaskdocTool,
230
231
  ];
package/dist/tool.d.ts CHANGED
@@ -50,7 +50,11 @@ type PreparedRawToolArguments = Readonly<{
50
50
  }>;
51
51
  export interface ReminderOptions {
52
52
  readonly echoback?: boolean;
53
- readonly scope?: ReminderScope;
53
+ readonly scope: ReminderScope;
54
+ readonly renderMode: ReminderRenderMode;
55
+ }
56
+ export interface ReminderUpdateOptions {
57
+ readonly echoback?: boolean;
54
58
  readonly renderMode?: ReminderRenderMode;
55
59
  }
56
60
  export type ReminderScope = 'dialog' | 'task' | 'agent' | 'runtime';
@@ -63,7 +67,6 @@ export interface Reminder extends ReminderOptions {
63
67
  readonly meta?: JsonValue;
64
68
  readonly createdAt?: string;
65
69
  readonly priority?: ReminderPriority;
66
- readonly renderMode?: ReminderRenderMode;
67
70
  }
68
71
  export declare function reminderEchoBackEnabled(reminder: Reminder): boolean;
69
72
  export declare function reminderIsVirtual(reminder: Reminder): boolean;
@@ -75,10 +78,10 @@ export declare function materializeReminder(input: Readonly<{
75
78
  owner?: ReminderOwner;
76
79
  meta?: JsonValue;
77
80
  echoback?: boolean;
78
- scope?: ReminderScope;
81
+ scope: ReminderScope;
79
82
  createdAt?: string;
80
83
  priority?: ReminderPriority;
81
- renderMode?: ReminderRenderMode;
84
+ renderMode: ReminderRenderMode;
82
85
  }>): Reminder;
83
86
  export declare function cloneReminder(reminder: Reminder): Reminder;
84
87
  export declare function computeReminderRenderRevision(reminder: Reminder): string;
package/dist/tool.js CHANGED
@@ -63,16 +63,22 @@ function ensureReminderId(value) {
63
63
  return trimmed && trimmed.length > 0 ? trimmed : generateReminderId();
64
64
  }
65
65
  function materializeReminder(input) {
66
+ if (input.scope === undefined) {
67
+ throw new Error('materializeReminder requires explicit scope');
68
+ }
69
+ if (input.renderMode === undefined) {
70
+ throw new Error('materializeReminder requires explicit renderMode');
71
+ }
66
72
  return {
67
73
  id: ensureReminderId(input.id),
68
74
  content: input.content,
69
75
  owner: input.owner,
70
76
  meta: input.meta,
71
77
  echoback: input.echoback,
72
- scope: input.scope ?? 'dialog',
78
+ scope: input.scope,
73
79
  createdAt: input.createdAt,
74
80
  priority: input.priority,
75
- renderMode: input.renderMode ?? 'markdown',
81
+ renderMode: input.renderMode,
76
82
  };
77
83
  }
78
84
  function cloneReminder(reminder) {
@@ -94,8 +100,8 @@ function computeReminderRenderRevision(reminder) {
94
100
  content: reminder.content,
95
101
  meta: reminder.meta ?? null,
96
102
  echoback: reminder.echoback ?? true,
97
- scope: reminder.scope ?? 'dialog',
98
- renderMode: reminder.renderMode ?? 'markdown',
103
+ scope: reminder.scope,
104
+ renderMode: reminder.renderMode,
99
105
  });
100
106
  return `sha256:${(0, crypto_1.createHash)('sha256').update(payload, 'utf8').digest('hex')}`;
101
107
  }
@@ -139,7 +139,7 @@ function toReminderState(reminder) {
139
139
  content: reminder.content,
140
140
  meta: extractOwnerMeta(reminder.meta),
141
141
  echoback: reminder.echoback,
142
- renderMode: reminder.renderMode ?? 'markdown',
142
+ renderMode: reminder.renderMode,
143
143
  };
144
144
  }
145
145
  function findOwnedReminderEntries(dlg, descriptor, owner) {
@@ -201,7 +201,7 @@ async function persistAndPublishReminders(dlg) {
201
201
  renderRevision: (0, tool_1.computeReminderRenderRevision)(reminder),
202
202
  echoback: (0, tool_1.reminderEchoBackEnabled)(reminder),
203
203
  scope: reminder.scope,
204
- renderMode: reminder.renderMode ?? 'markdown',
204
+ renderMode: reminder.renderMode,
205
205
  }));
206
206
  const evt = { type: 'full_reminders_update', reminders };
207
207
  (0, evt_registry_1.postDialogEvent)(dlg, evt);
@@ -321,6 +321,7 @@ async function applyAppReminderRequests(dlg, params) {
321
321
  case 'add': {
322
322
  dlg.addReminder(result.reminder.content, owner, buildAppReminderMeta(descriptor, result.reminder.meta), normalizeInsertPosition(dlg.reminders.length, result.position), {
323
323
  echoback: result.reminder.echoback,
324
+ scope: 'dialog',
324
325
  renderMode: result.reminder.renderMode ?? 'markdown',
325
326
  });
326
327
  changed = true;
@@ -334,7 +335,7 @@ async function applyAppReminderRequests(dlg, params) {
334
335
  dlg.updateReminder(target.index, result.reminder.content, buildAppReminderMeta(descriptor, result.reminder.meta), {
335
336
  echoback: result.reminder.echoback,
336
337
  // Preserve the existing render mode when the app does not explicitly override it.
337
- renderMode: result.reminder.renderMode ?? target.reminder.renderMode ?? 'markdown',
338
+ renderMode: result.reminder.renderMode ?? target.reminder.renderMode,
338
339
  });
339
340
  changed = true;
340
341
  break;
@@ -118,6 +118,7 @@ function manualSpecFor(toolsetId) {
118
118
  (0, registry_1.registerTool)(ctrl_1.addReminderTool);
119
119
  (0, registry_1.registerTool)(ctrl_1.deleteReminderTool);
120
120
  (0, registry_1.registerTool)(ctrl_1.updateReminderTool);
121
+ (0, registry_1.registerTool)(ctrl_1.migrateReminderTool);
121
122
  (0, registry_1.registerTool)(ctrl_1.clearMindTool);
122
123
  (0, registry_1.registerTool)(ctrl_1.doMindTool);
123
124
  (0, registry_1.registerTool)(ctrl_1.changeMindTool);
@@ -190,6 +191,7 @@ for (const tool of team_mgmt_1.teamMgmtTools) {
190
191
  ctrl_1.addReminderTool,
191
192
  ctrl_1.deleteReminderTool,
192
193
  ctrl_1.updateReminderTool,
194
+ ctrl_1.migrateReminderTool,
193
195
  ctrl_1.clearMindTool,
194
196
  ctrl_1.doMindTool,
195
197
  ctrl_1.changeMindTool,
@@ -1,3 +1,4 @@
1
+ export declare const MAX_CMD_RUNNER_OUTPUT_WAIT_TIMEOUT_MS: number;
1
2
  export type CmdRunnerSpawnSpec = Readonly<{
2
3
  command: string;
3
4
  args: string[];
@@ -38,6 +39,8 @@ export type CmdRunnerRequest = Readonly<{
38
39
  type: 'get_output';
39
40
  stdout: boolean;
40
41
  stderr: boolean;
42
+ waitForNewOutput: boolean;
43
+ timeoutMs?: number;
41
44
  }> | Readonly<{
42
45
  type: 'stop';
43
46
  entirePg: boolean;
@@ -45,7 +48,9 @@ export type CmdRunnerRequest = Readonly<{
45
48
  export type CmdRunnerStreamSnapshot = Readonly<{
46
49
  content: string;
47
50
  linesScrolledOut: number;
51
+ version: number;
48
52
  }>;
53
+ export type CmdRunnerOutputWaitStatus = 'output' | 'timeout' | 'exited';
49
54
  export type CmdRunnerStatusPayload = Readonly<{
50
55
  daemonPid: number;
51
56
  daemonCommandLine: string;
@@ -69,6 +74,7 @@ export type CmdRunnerResponse = (Readonly<{
69
74
  }> & CmdRunnerStatusPayload) | (Readonly<{
70
75
  type: 'output';
71
76
  ok: true;
77
+ waitStatus?: CmdRunnerOutputWaitStatus;
72
78
  }> & CmdRunnerStatusPayload) | (Readonly<{
73
79
  type: 'stop_result';
74
80
  ok: true;
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MAX_CMD_RUNNER_OUTPUT_WAIT_TIMEOUT_MS = void 0;
6
7
  exports.parseCmdRunnerInitMessage = parseCmdRunnerInitMessage;
7
8
  exports.parseCmdRunnerInitialIpcMessage = parseCmdRunnerInitialIpcMessage;
8
9
  exports.parseCmdRunnerRequestLine = parseCmdRunnerRequestLine;
@@ -10,6 +11,7 @@ exports.parseCmdRunnerResponseLine = parseCmdRunnerResponseLine;
10
11
  exports.getCmdRunnerEndpointForDaemonPid = getCmdRunnerEndpointForDaemonPid;
11
12
  const node_os_1 = __importDefault(require("node:os"));
12
13
  const node_path_1 = __importDefault(require("node:path"));
14
+ exports.MAX_CMD_RUNNER_OUTPUT_WAIT_TIMEOUT_MS = 24 * 60 * 60 * 1_000;
13
15
  function isRecord(value) {
14
16
  return typeof value === 'object' && value !== null && !Array.isArray(value);
15
17
  }
@@ -33,13 +35,39 @@ function parseStreamSnapshot(raw, label) {
33
35
  }
34
36
  const content = asString(raw['content']);
35
37
  const linesScrolledOut = asNumber(raw['linesScrolledOut']);
38
+ const version = asNumber(raw['version']);
36
39
  if (content === null) {
37
40
  throw new Error(`Invalid cmd_runner ${label}.content: expected string`);
38
41
  }
39
42
  if (linesScrolledOut === null) {
40
43
  throw new Error(`Invalid cmd_runner ${label}.linesScrolledOut: expected number`);
41
44
  }
42
- return { content, linesScrolledOut };
45
+ if (version === null) {
46
+ throw new Error(`Invalid cmd_runner ${label}.version: expected number`);
47
+ }
48
+ return { content, linesScrolledOut, version };
49
+ }
50
+ function parseOptionalTimeoutMs(raw, label) {
51
+ if (raw === undefined) {
52
+ return undefined;
53
+ }
54
+ const value = asNumber(raw);
55
+ if (value === null ||
56
+ !Number.isInteger(value) ||
57
+ value < 0 ||
58
+ value > exports.MAX_CMD_RUNNER_OUTPUT_WAIT_TIMEOUT_MS) {
59
+ throw new Error(`Invalid cmd_runner ${label}: expected non-negative integer <= ${String(exports.MAX_CMD_RUNNER_OUTPUT_WAIT_TIMEOUT_MS)}`);
60
+ }
61
+ return value;
62
+ }
63
+ function parseOutputWaitStatus(raw) {
64
+ if (raw === undefined) {
65
+ return undefined;
66
+ }
67
+ if (raw === 'output' || raw === 'timeout' || raw === 'exited') {
68
+ return raw;
69
+ }
70
+ throw new Error(`Invalid cmd_runner output waitStatus: ${String(raw)}`);
43
71
  }
44
72
  function parseStatusPayload(raw) {
45
73
  const daemonPid = asNumber(raw['daemonPid']);
@@ -239,10 +267,28 @@ function parseCmdRunnerRequestLine(line) {
239
267
  if (type === 'get_output') {
240
268
  const stdout = asBoolean(raw['stdout']);
241
269
  const stderr = asBoolean(raw['stderr']);
270
+ const waitForNewOutputRaw = raw['waitForNewOutput'];
271
+ const waitForNewOutput = waitForNewOutputRaw === undefined ? false : asBoolean(waitForNewOutputRaw);
272
+ const timeoutMs = parseOptionalTimeoutMs(raw['timeoutMs'], 'get_output.timeoutMs');
242
273
  if (stdout === null || stderr === null) {
243
274
  throw new Error('Invalid cmd_runner get_output request: stdout/stderr must be boolean');
244
275
  }
245
- return { type, stdout, stderr };
276
+ if (waitForNewOutput === null) {
277
+ throw new Error('Invalid cmd_runner get_output request: waitForNewOutput must be boolean');
278
+ }
279
+ if (!stdout && !stderr) {
280
+ throw new Error('Invalid cmd_runner get_output request: at least one stream is required');
281
+ }
282
+ if (!waitForNewOutput && timeoutMs !== undefined) {
283
+ throw new Error('Invalid cmd_runner get_output request: timeoutMs requires waitForNewOutput=true');
284
+ }
285
+ return {
286
+ type,
287
+ stdout,
288
+ stderr,
289
+ waitForNewOutput,
290
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
291
+ };
246
292
  }
247
293
  throw new Error(`Invalid cmd_runner request type: ${String(type)}`);
248
294
  }
@@ -269,6 +315,15 @@ function parseCmdRunnerResponseLine(line) {
269
315
  if (type !== 'pong' && type !== 'status' && type !== 'output' && type !== 'stop_result') {
270
316
  throw new Error(`Invalid cmd_runner response type: ${String(type)}`);
271
317
  }
318
+ if (type === 'output') {
319
+ const waitStatus = parseOutputWaitStatus(raw['waitStatus']);
320
+ return {
321
+ type,
322
+ ok: true,
323
+ ...(waitStatus === undefined ? {} : { waitStatus }),
324
+ ...parseStatusPayload(raw),
325
+ };
326
+ }
272
327
  return {
273
328
  type,
274
329
  ok: true,
@@ -17,10 +17,15 @@ class ScrollingBuffer {
17
17
  maxLines;
18
18
  lines = [];
19
19
  linesScrolledOut = 0;
20
+ version = 0;
20
21
  constructor(maxLines) {
21
22
  this.maxLines = maxLines;
22
23
  }
23
24
  addText(text) {
25
+ if (text.length === 0) {
26
+ return;
27
+ }
28
+ this.version += 1;
24
29
  const newLines = text.split('\n');
25
30
  if (newLines[newLines.length - 1] === '') {
26
31
  newLines.pop();
@@ -37,8 +42,12 @@ class ScrollingBuffer {
37
42
  return {
38
43
  content: this.lines.join('\n'),
39
44
  linesScrolledOut: this.linesScrolledOut,
45
+ version: this.version,
40
46
  };
41
47
  }
48
+ getVersion() {
49
+ return this.version;
50
+ }
42
51
  }
43
52
  async function flushIpc(msg) {
44
53
  const send = process.send;
@@ -177,6 +186,64 @@ async function main() {
177
186
  let closeRequested = false;
178
187
  let timeoutHandle;
179
188
  let initialResultSent = false;
189
+ const outputWaiters = [];
190
+ const requestedOutputChanged = (waiter) => (waiter.stdout && state.stdout.getVersion() !== waiter.initialStdoutVersion) ||
191
+ (waiter.stderr && state.stderr.getVersion() !== waiter.initialStderrVersion);
192
+ const removeOutputWaiter = (waiter) => {
193
+ const index = outputWaiters.indexOf(waiter);
194
+ if (index !== -1) {
195
+ outputWaiters.splice(index, 1);
196
+ }
197
+ };
198
+ const settleOutputWaiter = (waiter, status) => {
199
+ removeOutputWaiter(waiter);
200
+ if (waiter.timeoutHandle !== undefined) {
201
+ clearTimeout(waiter.timeoutHandle);
202
+ }
203
+ waiter.resolve(status);
204
+ };
205
+ const notifyOutputWaiters = () => {
206
+ for (const waiter of [...outputWaiters]) {
207
+ if (requestedOutputChanged(waiter)) {
208
+ settleOutputWaiter(waiter, 'output');
209
+ continue;
210
+ }
211
+ if (!state.isRunning) {
212
+ settleOutputWaiter(waiter, 'exited');
213
+ }
214
+ }
215
+ };
216
+ const waitForRequestedNewOutput = async (request, socket) => {
217
+ if (!state.isRunning) {
218
+ return 'exited';
219
+ }
220
+ return await new Promise((resolve) => {
221
+ let waiter;
222
+ const onSocketClosed = () => {
223
+ if (waiter !== undefined) {
224
+ settleOutputWaiter(waiter, 'client_closed');
225
+ }
226
+ };
227
+ waiter = {
228
+ stdout: request.stdout,
229
+ stderr: request.stderr,
230
+ initialStdoutVersion: state.stdout.getVersion(),
231
+ initialStderrVersion: state.stderr.getVersion(),
232
+ resolve: (status) => {
233
+ socket.off('close', onSocketClosed);
234
+ resolve(status);
235
+ },
236
+ };
237
+ if (request.timeoutMs !== undefined) {
238
+ waiter.timeoutHandle = setTimeout(() => {
239
+ settleOutputWaiter(waiter, 'timeout');
240
+ }, request.timeoutMs);
241
+ }
242
+ socket.once('close', onSocketClosed);
243
+ outputWaiters.push(waiter);
244
+ notifyOutputWaiters();
245
+ });
246
+ };
180
247
  const tryFlushInitialResult = async (msg) => {
181
248
  if (initialResultSent) {
182
249
  return false;
@@ -262,6 +329,7 @@ async function main() {
262
329
  state.isRunning = false;
263
330
  state.exitCode = code;
264
331
  state.exitSignal = signal;
332
+ notifyOutputWaiters();
265
333
  void (async () => {
266
334
  if (state.daemonCommandLine === null && !initialResultSent) {
267
335
  await tryFlushInitialResult({
@@ -292,9 +360,11 @@ async function main() {
292
360
  });
293
361
  stdout.on('data', (data) => {
294
362
  state.stdout.addText(data.toString());
363
+ notifyOutputWaiters();
295
364
  });
296
365
  stderr.on('data', (data) => {
297
366
  state.stderr.addText(data.toString());
367
+ notifyOutputWaiters();
298
368
  });
299
369
  await ensureSocketParentDir(endpoint);
300
370
  if (closeRequested) {
@@ -331,13 +401,24 @@ async function main() {
331
401
  return;
332
402
  }
333
403
  if (request.type === 'get_output') {
404
+ const waitStatus = request.waitForNewOutput
405
+ ? await waitForRequestedNewOutput(request, socket)
406
+ : undefined;
407
+ if (waitStatus === 'client_closed') {
408
+ return;
409
+ }
334
410
  const payload = buildStatusPayload(state);
335
411
  writeSocketResponse(socket, {
336
412
  type: 'output',
337
413
  ok: true,
414
+ ...(waitStatus === undefined ? {} : { waitStatus }),
338
415
  ...payload,
339
- stdout: request.stdout ? payload.stdout : { content: '', linesScrolledOut: 0 },
340
- stderr: request.stderr ? payload.stderr : { content: '', linesScrolledOut: 0 },
416
+ stdout: request.stdout
417
+ ? payload.stdout
418
+ : { content: '', linesScrolledOut: 0, version: 0 },
419
+ stderr: request.stderr
420
+ ? payload.stderr
421
+ : { content: '', linesScrolledOut: 0, version: 0 },
341
422
  });
342
423
  return;
343
424
  }
@@ -12,6 +12,7 @@
12
12
  * - add_reminder: Add a reminder
13
13
  * - delete_reminder: Delete a reminder by id
14
14
  * - update_reminder: Update reminder content
15
+ * - migrate_reminder: Move a visible shared reminder back into the current dialog
15
16
  * - clear_mind: Start a new course, optionally add a reminder
16
17
  * - do_mind: Main Dialog only; create a new `.tsk/` Taskdoc section without starting a new course
17
18
  * - change_mind: Main Dialog only; update a `.tsk/` Taskdoc section without starting a new course
@@ -32,6 +33,7 @@ import { type FuncTool } from '../tool';
32
33
  export declare const deleteReminderTool: FuncTool;
33
34
  export declare const addReminderTool: FuncTool;
34
35
  export declare const updateReminderTool: FuncTool;
36
+ export declare const migrateReminderTool: FuncTool;
35
37
  export declare const clearMindTool: FuncTool;
36
38
  export declare const changeMindTool: FuncTool;
37
39
  export declare const doMindTool: FuncTool;