agentic-workflow-manager 9.16.0 → 9.16.1

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.
@@ -470,7 +470,10 @@ function descendantActivity(psTable, rootPid) {
470
470
  const children = new Map();
471
471
  let rows = 0;
472
472
  for (const line of psTable.split('\n')) {
473
- const match = /^\s*(\d+)\s+(\d+)\s+(\S+)\s*$/.exec(line);
473
+ // time may be empty on some Darwin samples for a just-spawned process;
474
+ // treat blank as "0" so the pid still joins the descendant tree (otherwise
475
+ // a healthy detached grandchild is invisible and activity reads as frozen).
476
+ const match = /^\s*(\d+)\s+(\d+)(?:\s+(\S+))?\s*$/.exec(line);
474
477
  if (match === null)
475
478
  continue;
476
479
  if (++rows > MAX_PS_ROWS)
@@ -478,7 +481,7 @@ function descendantActivity(psTable, rootPid) {
478
481
  const pid = Number(match[1]);
479
482
  const ppid = Number(match[2]);
480
483
  const siblings = children.get(ppid) ?? [];
481
- siblings.push({ pid, time: match[3] });
484
+ siblings.push({ pid, time: match[3] && match[3].length > 0 ? match[3] : '0' });
482
485
  children.set(ppid, siblings);
483
486
  }
484
487
  const seen = new Set([rootPid]);
@@ -499,13 +502,24 @@ function descendantActivity(psTable, rootPid) {
499
502
  return `${found.length}:${crypto_1.default.createHash('sha256').update(found.join('\n')).digest('hex')}`;
500
503
  }
501
504
  function observeDescendants(rootPid) {
502
- try {
503
- const table = (0, child_process_1.execFileSync)('ps', ['-A', '-o', 'pid=,ppid=,time='], { encoding: 'utf8', stdio: exports.EXEC_STDIO, timeout: PS_TIMEOUT_MS, maxBuffer: 16 * 1024 * 1024 });
504
- return descendantActivity(table, rootPid);
505
- }
506
- catch {
507
- return 'unknown';
505
+ // One transient `ps` failure under CI load used to freeze the snapshot at
506
+ // `unknown`, which made the Darwin integration test (and a real supervisor
507
+ // tick) treat a later concrete `0:none` as "activity changed" without ever
508
+ // seeing the grandchild. Retry once before degrading.
509
+ let lastError;
510
+ for (let attempt = 0; attempt < 2; attempt++) {
511
+ try {
512
+ const table = (0, child_process_1.execFileSync)('ps', ['-A', '-o', 'pid=,ppid=,time='], {
513
+ encoding: 'utf8', stdio: exports.EXEC_STDIO, timeout: PS_TIMEOUT_MS, maxBuffer: 16 * 1024 * 1024,
514
+ });
515
+ return descendantActivity(table, rootPid);
516
+ }
517
+ catch (error) {
518
+ lastError = error;
519
+ }
508
520
  }
521
+ void lastError;
522
+ return 'unknown';
509
523
  }
510
524
  /** Llamada UNA VEZ POR TICK por el supervisor (ver superviseController en
511
525
  * commands/watch/supervisor.ts) mientras un controlador este activo — el
@@ -202,28 +202,51 @@ describe('process identity', () => {
202
202
  test('ignores malformed ps lines instead of inventing processes', () => {
203
203
  expect((0, process_1.descendantActivity)('garbage\n 20 10 00:00:01\n\n x y z', 10)).toMatch(/^1:/);
204
204
  });
205
+ test('keeps Darwin-style rows with blank or dotted cputime in the tree', () => {
206
+ // macOS `ps -o time=` is often `0:00.01`; a brand-new process can also
207
+ // omit the third column entirely on some samples.
208
+ expect((0, process_1.descendantActivity)(' 10 1 0:00.05\n 20 10 0:00.01\n 30 20\n', 10)).toMatch(/^2:/);
209
+ expect((0, process_1.descendantActivity)(' 10 1\n 20 10\n', 10)).toMatch(/^1:/);
210
+ });
205
211
  });
206
212
  test('activitySnapshot ve un nieto fuera del process group del controlador', async () => {
207
213
  if ((0, paths_1.isWindowsNative)())
208
214
  return; // win32 degrada activity sin ps (ver activitySnapshot)
209
- const script = "setTimeout(() => { require('child_process').spawn(process.execPath, ['-e', 'setTimeout(()=>{}, 4000)'], { detached: true, stdio: 'ignore' }).unref(); }, 400); setTimeout(()=>{}, 4000)";
215
+ const script = "setTimeout(() => { require('child_process').spawn(process.execPath, ['-e', 'setTimeout(()=>{}, 15000)'], { detached: true, stdio: 'ignore' }).unref(); }, 200); setTimeout(()=>{}, 15000)";
210
216
  const { child, ref } = (0, process_1.spawnStructured)(['node', '-e', script], process.cwd(), 'n-desc');
211
217
  try {
212
- const before = (0, process_1.activitySnapshot)(ref);
213
- expect(before).not.toBeNull();
214
- let after = (0, process_1.activitySnapshot)(ref);
215
- for (let i = 0; i < 40 && after !== null && after.descendants === before.descendants; i++) {
218
+ // Wait for a concrete baseline (not `unknown` from a transient ps miss).
219
+ let before = (0, process_1.activitySnapshot)(ref);
220
+ for (let i = 0; i < 50 && (before === null || before.descendants === 'unknown'); i++) {
216
221
  await new Promise((resolve) => setTimeout(resolve, 50));
217
- after = (0, process_1.activitySnapshot)(ref);
222
+ before = (0, process_1.activitySnapshot)(ref);
223
+ }
224
+ expect(before).not.toBeNull();
225
+ expect(before.descendants).not.toBe('unknown');
226
+ const beforeGroup = before.groupSize;
227
+ let after = before;
228
+ const deadline = Date.now() + 8000;
229
+ while (Date.now() < deadline) {
230
+ const snap = (0, process_1.activitySnapshot)(ref);
231
+ if (snap !== null)
232
+ after = snap;
233
+ // Success: same process group, at least one PPID-descendant appeared.
234
+ if (after.groupSize === beforeGroup && /^[1-9]\d*:/.test(after.descendants))
235
+ break;
236
+ await new Promise((resolve) => setTimeout(resolve, 100));
218
237
  }
219
- expect(after).not.toBeNull();
220
238
  // The detached grandchild lives in its own process group: the group
221
239
  // signal cannot see it, the descendant signal must.
222
- expect(after.groupSize).toBe(before.groupSize);
223
- expect(after.descendants).not.toBe(before.descendants);
224
- expect(after.descendants).toMatch(/^1:/);
240
+ expect(after.groupSize).toBe(beforeGroup);
241
+ expect(after.descendants).toMatch(/^[1-9]\d*:/);
225
242
  }
226
243
  finally {
244
+ // Detached grandchildren are outside `-PGID`; reap by parent pid too.
245
+ try {
246
+ const { execFileSync } = require('child_process');
247
+ execFileSync('pkill', ['-P', String(ref.pid)], { stdio: 'ignore', timeout: 1000 });
248
+ }
249
+ catch { /* ya ausente */ }
227
250
  try {
228
251
  process.kill(-ref.processGroup, 'SIGKILL');
229
252
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-workflow-manager",
3
- "version": "9.16.0",
3
+ "version": "9.16.1",
4
4
  "main": "dist/src/index.js",
5
5
  "bin": {
6
6
  "awm": "./dist/src/index.js"
Binary file
Binary file