dominds 1.25.4 → 1.25.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -204,6 +204,11 @@ function getMemoryPromptCopy(ctx) {
204
204
  ? `工作流:先做事 → 再提炼(\`update_reminder\`;必要时整理差遣牒追加条目/更新提案并诉请 \`@${ctx.taskdocMaintainerId}\` 合并写入)→ 然后 \`clear_mind\` 清空噪音。`
205
205
  : '工作流:停止扩张上下文 → 维护足够详尽的接续包提醒项(`add_reminder` 或 `update_reminder`,长度没有技术限制)→ 然后 `clear_mind` 开启新一程。',
206
206
  mainDialogWorkflowLine: '工作流:先做事 -> 再提炼(`update_reminder` + `mind_more(progress)`;需要压缩/删旧时先 `recall_taskdoc` 取得 `content_hash`,再用带 `previous_content_hash` 的 `change_mind`;要删除整章文件时用 `never_mind`)-> 然后 `clear_mind` 清空噪音。',
207
+ ...(ctx.isSideDialog
208
+ ? {}
209
+ : {
210
+ progressVcsOrderLine: '硬性顺序:先补 `progress`,再动 git。只要这次代码/文档改动准备进 git(add / commit / push),先确认 `progress` 已经写清当前状态、决策、阻塞和下一步;如果还没写清,就先补写。简单判断:改动会进仓库 + `progress` 还没跟上 = 先别动 git。`git status` 只用来确认,不是提交动作。不要把“提交了某个 commit”“push 了”写进 `progress`。',
211
+ }),
207
212
  contextHealthLine: contextHealthLineZh,
208
213
  taskdocLogLine: taskdocLogLineZh,
209
214
  };
@@ -239,6 +244,11 @@ function getMemoryPromptCopy(ctx) {
239
244
  ? `Workflow: do work → distill (\`update_reminder\`; when Taskdoc needs updates, draft append entries, a merged replacement, or a section deletion and ask \`@${ctx.taskdocMaintainerId}\`) → then \`clear_mind\` to drop noise.`
240
245
  : 'Workflow: stop expanding context → maintain sufficiently detailed continuation-package reminders (`add_reminder` or `update_reminder`, with no technical length limit) → then `clear_mind` to start a new course.',
241
246
  mainDialogWorkflowLine: 'Workflow: do work -> distill (`update_reminder` + `mind_more(progress)`; when compression/deletion is needed, first use `recall_taskdoc` to get `content_hash`, then use `change_mind` with `previous_content_hash`; use `never_mind` when removing a whole section file) -> then `clear_mind` to drop noise.',
247
+ ...(ctx.isSideDialog
248
+ ? {}
249
+ : {
250
+ progressVcsOrderLine: 'Hard order: update `progress` first, then use git. If this code/docs change is going into git (add / commit / push), make sure `progress` already says the current state, decisions, blockers, and next step; if not, write it first. Simple check: change will enter the repo + `progress` is behind = stop and update `progress` first. `git status` is only for checking, not a commit step. Do not write git events like “committed X” or “ran git push” into progress.',
251
+ }),
242
252
  contextHealthLine: contextHealthLineEn,
243
253
  taskdocLogLine: taskdocLogLineEn,
244
254
  };
@@ -264,6 +274,7 @@ function buildMemorySystemPrompt(ctx) {
264
274
  ...(ctx.agentHasTeamMemoryTools ? [copy.teamMemoryHintLine] : []),
265
275
  ...(ctx.agentHasPersonalMemoryTools ? [copy.personalMemoryHintLine] : []),
266
276
  ctx.isSideDialog ? copy.sideDialogWorkflowLine : copy.mainDialogWorkflowLine,
277
+ ...(copy.progressVcsOrderLine === undefined ? [] : [copy.progressVcsOrderLine]),
267
278
  copy.contextHealthLine,
268
279
  copy.taskdocLogLine,
269
280
  ].join('\n');
@@ -16,9 +16,12 @@ const util_1 = require("util");
16
16
  const time_1 = require("@longrun-ai/kernel/utils/time");
17
17
  const log_1 = require("../log");
18
18
  const dominds_running_version_1 = require("./dominds-running-version");
19
- const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
19
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
20
20
  const log = (0, log_1.createLogger)('dominds-self-update');
21
21
  const BACKGROUND_CHECK_INTERVAL_MS = 30 * 60 * 1000;
22
+ const LATEST_VERSION_CHECK_TIMEOUT_MS = 15000;
23
+ const RESTART_PORT_RELEASE_TIMEOUT_MS = 15000;
24
+ const RESTART_PORT_PROBE_INTERVAL_MS = 150;
22
25
  const IDLE_RESTART_STATE = { kind: 'idle' };
23
26
  let runtimeConfig = null;
24
27
  let latestObservation = { kind: 'unknown' };
@@ -27,12 +30,6 @@ let installPromise = null;
27
30
  let restartPromise = null;
28
31
  let restartState = IDLE_RESTART_STATE;
29
32
  let broadcastStatusUpdate = null;
30
- function getNpmBin() {
31
- return process.platform === 'win32' ? 'npm.cmd' : 'npm';
32
- }
33
- function getNpxBin() {
34
- return process.platform === 'win32' ? 'npx.cmd' : 'npx';
35
- }
36
33
  function normalizeVersionString(value) {
37
34
  return value.trim().replace(/^v/i, '');
38
35
  }
@@ -74,17 +71,31 @@ function detectRunKind(mode) {
74
71
  }
75
72
  return 'npm_global';
76
73
  }
74
+ function hasInteractiveConsole() {
75
+ return Boolean(process.stdin.isTTY || process.stdout.isTTY || process.stderr.isTTY);
76
+ }
77
+ function getRestartHelperStdio() {
78
+ return hasInteractiveConsole() ? 'inherit' : 'ignore';
79
+ }
80
+ function getRestartPortProbeHost(host) {
81
+ if (host === '0.0.0.0')
82
+ return '127.0.0.1';
83
+ if (host === '::')
84
+ return '::1';
85
+ return host;
86
+ }
77
87
  async function queryLatestVersion() {
78
88
  const checkedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
79
89
  try {
80
- const { stdout } = await execFileAsync(getNpmBin(), ['view', 'dominds', 'version', '--json'], {
90
+ const { stdout } = await execAsync('npm view dominds version --json', {
81
91
  cwd: process.cwd(),
82
92
  env: process.env,
83
93
  maxBuffer: 1024 * 1024,
84
- timeout: 15000,
94
+ timeout: LATEST_VERSION_CHECK_TIMEOUT_MS,
85
95
  });
86
96
  const trimmed = stdout.trim();
87
97
  if (trimmed === '') {
98
+ log.warn('Dominds latest-version check returned empty stdout', undefined, { checkedAt });
88
99
  return {
89
100
  kind: 'error',
90
101
  errorText: 'npm view dominds version returned empty stdout',
@@ -100,6 +111,10 @@ async function queryLatestVersion() {
100
111
  }
101
112
  const latestVersion = typeof parsed === 'string' ? parsed.trim() : '';
102
113
  if (latestVersion === '') {
114
+ log.warn('Dominds latest-version check returned an invalid payload', undefined, {
115
+ checkedAt,
116
+ stdout: trimmed,
117
+ });
103
118
  return {
104
119
  kind: 'error',
105
120
  errorText: `npm view dominds version returned non-string output: ${trimmed}`,
@@ -109,15 +124,21 @@ async function queryLatestVersion() {
109
124
  return { kind: 'ok', latestVersion, checkedAt };
110
125
  }
111
126
  catch (error) {
127
+ const errorText = error instanceof Error && error.name === 'AbortError'
128
+ ? 'npm view dominds version timed out'
129
+ : error instanceof Error
130
+ ? error.message
131
+ : String(error);
132
+ log.warn('Dominds latest-version check failed', error, { checkedAt, errorText });
112
133
  return {
113
134
  kind: 'error',
114
- errorText: error instanceof Error ? error.message : String(error),
135
+ errorText,
115
136
  checkedAt,
116
137
  };
117
138
  }
118
139
  }
119
140
  async function resolveGlobalDomindsCommandAbs() {
120
- const { stdout } = await execFileAsync(getNpmBin(), ['prefix', '-g'], {
141
+ const { stdout } = await execAsync('npm prefix -g', {
121
142
  cwd: process.cwd(),
122
143
  env: process.env,
123
144
  maxBuffer: 1024 * 1024,
@@ -422,7 +443,7 @@ async function installLatestDominds() {
422
443
  if (!hasUpdate) {
423
444
  throw new Error('No installable Dominds update is currently available');
424
445
  }
425
- await execFileAsync(getNpmBin(), ['i', '-g', 'dominds@latest'], {
446
+ await execAsync('npm i -g dominds@latest', {
426
447
  cwd: process.cwd(),
427
448
  env: process.env,
428
449
  maxBuffer: 20 * 1024 * 1024,
@@ -435,7 +456,19 @@ async function installLatestDominds() {
435
456
  globalCommandAbs,
436
457
  };
437
458
  return await getDomindsSelfUpdateStatus();
438
- })().finally(() => {
459
+ })()
460
+ .catch((error) => {
461
+ const latestVersion = latestObservation.kind === 'ok' ? latestObservation.latestVersion : null;
462
+ const checkedAt = latestObservation.kind === 'unknown' ? null : latestObservation.checkedAt;
463
+ log.error('Dominds version install failed', error, {
464
+ runKind,
465
+ currentVersion: getRunningVersion(),
466
+ latestVersion,
467
+ checkedAt,
468
+ });
469
+ throw error;
470
+ })
471
+ .finally(() => {
439
472
  installPromise = null;
440
473
  publishStatusUpdateSoon();
441
474
  });
@@ -446,33 +479,72 @@ function buildRestartArgs(cfg) {
446
479
  return ['webui', '-p', String(cfg.port), '-h', cfg.host, '--mode', 'prod', '--nobrowser'];
447
480
  }
448
481
  function spawnDetachedRestartHelper(params) {
482
+ const stdioMode = getRestartHelperStdio();
449
483
  const helperPayload = JSON.stringify({
450
484
  command: params.command,
451
485
  args: [...params.args],
452
486
  cwd: params.cwd,
453
- delayMs: 800,
487
+ host: getRestartPortProbeHost(params.host),
488
+ port: params.port,
489
+ probeIntervalMs: RESTART_PORT_PROBE_INTERVAL_MS,
490
+ portReleaseTimeoutMs: RESTART_PORT_RELEASE_TIMEOUT_MS,
491
+ stdioMode,
454
492
  });
455
493
  const helperScript = [
494
+ "const net = require('net');",
456
495
  "const { spawn } = require('child_process');",
457
496
  'const payload = JSON.parse(process.argv[1]);',
458
- 'setTimeout(() => {',
497
+ 'const detached = payload.stdioMode !== "inherit" || process.platform === "win32";',
498
+ 'function isPortBusy() {',
499
+ ' return new Promise((resolve) => {',
500
+ ' const socket = net.createConnection({ host: payload.host, port: payload.port });',
501
+ ' let settled = false;',
502
+ ' const finish = (busy) => {',
503
+ ' if (settled) return;',
504
+ ' settled = true;',
505
+ ' socket.destroy();',
506
+ ' resolve(busy);',
507
+ ' };',
508
+ ' socket.once("connect", () => finish(true));',
509
+ ' socket.once("error", () => finish(false));',
510
+ ' socket.setTimeout(1000, () => finish(true));',
511
+ ' });',
512
+ '}',
513
+ 'async function waitForPortRelease() {',
514
+ ' const deadline = Date.now() + payload.portReleaseTimeoutMs;',
515
+ ' let consecutiveIdle = 0;',
516
+ ' while (Date.now() < deadline) {',
517
+ ' if (await isPortBusy()) {',
518
+ ' consecutiveIdle = 0;',
519
+ ' await new Promise((resolve) => setTimeout(resolve, payload.probeIntervalMs));',
520
+ ' continue;',
521
+ ' }',
522
+ ' consecutiveIdle += 1;',
523
+ ' if (consecutiveIdle >= 2) return;',
524
+ ' await new Promise((resolve) => setTimeout(resolve, payload.probeIntervalMs));',
525
+ ' }',
526
+ '}',
527
+ '(async () => {',
459
528
  ' try {',
460
- " const child = spawn(payload.command, payload.args, { cwd: payload.cwd, env: process.env, detached: true, stdio: 'ignore' });",
461
- ' child.unref();',
529
+ ' await waitForPortRelease();',
530
+ " const child = spawn(payload.command, payload.args, { cwd: payload.cwd, env: process.env, detached, stdio: payload.stdioMode, shell: process.platform === 'win32' });",
531
+ ' if (detached) child.unref();',
462
532
  ' process.exit(0);',
463
533
  ' } catch (error) {',
464
534
  ' console.error(error instanceof Error ? error.message : String(error));',
465
535
  ' process.exit(1);',
466
536
  ' }',
467
- '}, payload.delayMs);',
537
+ '})();',
468
538
  ].join('\n');
469
539
  const helper = (0, child_process_1.spawn)(process.execPath, ['-e', helperScript, helperPayload], {
470
540
  cwd: params.cwd,
471
541
  env: process.env,
472
- detached: true,
473
- stdio: 'ignore',
542
+ detached: stdioMode !== 'inherit' || process.platform === 'win32',
543
+ stdio: stdioMode,
474
544
  });
475
- helper.unref();
545
+ if (stdioMode !== 'inherit' || process.platform === 'win32') {
546
+ helper.unref();
547
+ }
476
548
  }
477
549
  async function stopAndExitForRestart() {
478
550
  const cfg = assertRuntimeConfig();
@@ -500,7 +572,7 @@ async function restartDomindsIntoLatest() {
500
572
  let command;
501
573
  const previousRestartRequiredState = restartState.kind === 'restart_required' ? restartState : null;
502
574
  if (runKind === 'npx_latest') {
503
- command = getNpxBin();
575
+ command = 'npx';
504
576
  args.unshift('dominds@latest');
505
577
  args.unshift('-y');
506
578
  }
@@ -512,25 +584,48 @@ async function restartDomindsIntoLatest() {
512
584
  }
513
585
  restartState = { kind: 'restarting', targetVersion: status.targetVersion };
514
586
  publishStatusUpdateSoon();
515
- spawnDetachedRestartHelper({
516
- command,
517
- args,
518
- cwd: process.cwd(),
519
- });
520
- setImmediate(() => {
521
- void stopAndExitForRestart().catch((error) => {
522
- log.error('Failed to stop Dominds server during restart', error);
523
- if (runKind === 'npm_global' && previousRestartRequiredState !== null) {
524
- restartState = previousRestartRequiredState;
587
+ try {
588
+ spawnDetachedRestartHelper({
589
+ command,
590
+ args,
591
+ cwd: process.cwd(),
592
+ host: cfg.host,
593
+ port: cfg.port,
594
+ });
595
+ setImmediate(() => {
596
+ void stopAndExitForRestart().catch((error) => {
597
+ log.error('Failed to stop Dominds server during restart', error);
598
+ if (runKind === 'npm_global' && previousRestartRequiredState !== null) {
599
+ restartState = previousRestartRequiredState;
600
+ publishStatusUpdateSoon();
601
+ return;
602
+ }
603
+ restartState = IDLE_RESTART_STATE;
525
604
  publishStatusUpdateSoon();
526
- return;
527
- }
528
- restartState = IDLE_RESTART_STATE;
529
- publishStatusUpdateSoon();
605
+ });
530
606
  });
531
- });
607
+ }
608
+ catch (error) {
609
+ if (previousRestartRequiredState !== null) {
610
+ restartState = previousRestartRequiredState;
611
+ }
612
+ else {
613
+ restartState = IDLE_RESTART_STATE;
614
+ }
615
+ publishStatusUpdateSoon();
616
+ throw error;
617
+ }
532
618
  return await getDomindsSelfUpdateStatus();
533
- })().finally(() => {
619
+ })()
620
+ .catch((error) => {
621
+ const statusSnapshot = restartState.kind === 'restarting' ? restartState : null;
622
+ log.error('Dominds version restart failed', error, {
623
+ runKind: detectRunKind(cfg.mode),
624
+ restartState: statusSnapshot,
625
+ });
626
+ throw error;
627
+ })
628
+ .finally(() => {
534
629
  restartPromise = null;
535
630
  publishStatusUpdateSoon();
536
631
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dominds",
3
- "version": "1.25.4",
3
+ "version": "1.25.5",
4
4
  "description": "Dominds CLI and aggregation shell for the LongRun AI kernel/runtime packages.",
5
5
  "type": "commonjs",
6
6
  "publishConfig": {
@@ -53,8 +53,8 @@
53
53
  "yaml": "^2.8.2",
54
54
  "zod": "^4.3.6",
55
55
  "@longrun-ai/codex-auth": "0.13.0",
56
- "@longrun-ai/kernel": "1.15.4",
57
- "@longrun-ai/shell": "1.15.4"
56
+ "@longrun-ai/kernel": "1.15.5",
57
+ "@longrun-ai/shell": "1.15.5"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@types/node": "^25.3.5",