agentic-workflow-manager 9.12.0 → 9.12.2
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/src/commands/job/gate.js +6 -1
- package/dist/src/commands/job/index.js +22 -1
- package/dist/src/commands/watch/admission-recovery.js +161 -0
- package/dist/src/commands/watch/apply.js +7 -1
- package/dist/src/commands/watch/archive-unused.js +2 -0
- package/dist/src/commands/watch/index.js +55 -0
- package/dist/src/commands/watch/init.js +2 -1
- package/dist/src/commands/watch/request-recovery.js +153 -0
- package/dist/src/commands/watch/supervisor.js +105 -46
- package/dist/src/core/journal/requests.js +1 -0
- package/dist/src/core/journal/types.js +67 -2
- package/dist/tests/commands/watch/admission-recovery.test.js +278 -0
- package/dist/tests/commands/watch/request-recovery.test.js +253 -0
- package/dist/tests/commands/watch/supervisor-loop.test.js +88 -0
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/secure_fs.node +0 -0
- package/prebuilds/darwin-x64/secure_fs.node +0 -0
- package/prebuilds/win32-arm64/secure_fs.node +0 -0
- package/prebuilds/win32-x64/secure_fs.node +0 -0
|
@@ -416,9 +416,12 @@ describe('supervisor loop', () => {
|
|
|
416
416
|
});
|
|
417
417
|
let spawned = 0;
|
|
418
418
|
const sup = new supervisor_1.Supervisor(repo, 'main', supervisor_1.DEFAULT_SUPERVISOR_CONFIG, () => { spawned += 1; }, undefined, notCertified);
|
|
419
|
+
let now = Date.now();
|
|
420
|
+
jest.spyOn(Date, 'now').mockImplementation(() => now);
|
|
419
421
|
for (let i = 0; i < supervisor_1.MAX_SENSOR_DEFERRALS; i += 1) {
|
|
420
422
|
expect(await sup.tick()).toBe('continue');
|
|
421
423
|
expect((0, store_1.readJournal)(repo, 'main').state.cycle.status).toBe('IN_PROGRESS');
|
|
424
|
+
now += 5000;
|
|
422
425
|
}
|
|
423
426
|
// The verdict never settled, so the supervisor must stop pretending it might.
|
|
424
427
|
expect(await sup.tick()).toBe('custody');
|
|
@@ -443,6 +446,91 @@ describe('supervisor loop', () => {
|
|
|
443
446
|
// Deferring dispatch was right throughout, and stays right.
|
|
444
447
|
expect(spawned).toBe(0);
|
|
445
448
|
});
|
|
449
|
+
test('unverifiable currentness retries durably across supervisor restart and does not spawn before admitted', async () => {
|
|
450
|
+
initUnattendedFixture(repo);
|
|
451
|
+
let now = Date.now();
|
|
452
|
+
jest.spyOn(Date, 'now').mockImplementation(() => now);
|
|
453
|
+
let verdict = 'unverifiable';
|
|
454
|
+
const observe = async () => verdict === 'unverifiable'
|
|
455
|
+
? { state: 'blocked', planState: 'valid', executionMode: 'desatendido', journal: 'not-required', currentness: 'unverifiable', sensors: 'not-required', diagnostics: [{ code: 'ADMISSION_CURRENTNESS_BLOCKED', message: 'registry:baseline' }] }
|
|
456
|
+
: admitted();
|
|
457
|
+
let spawned = 0;
|
|
458
|
+
const spawn = () => { spawned += 1; };
|
|
459
|
+
expect(await new supervisor_1.Supervisor(repo, 'main', supervisor_1.DEFAULT_SUPERVISOR_CONFIG, spawn, undefined, observe, true).tick()).toBe('continue');
|
|
460
|
+
expect((0, store_1.readJournal)(repo, 'main').state).toHaveProperty('admissionRetry.attempts', 1);
|
|
461
|
+
expect((0, store_1.readJournal)(repo, 'main').state.generations).toHaveLength(0);
|
|
462
|
+
expect(spawned).toBe(0);
|
|
463
|
+
// New supervisor instance represents a process restart. Repeated early
|
|
464
|
+
// ticks cannot spend or reset the durable retry budget.
|
|
465
|
+
const restarted = new supervisor_1.Supervisor(repo, 'main', supervisor_1.DEFAULT_SUPERVISOR_CONFIG, spawn, undefined, observe, true);
|
|
466
|
+
expect(await restarted.tick()).toBe('continue');
|
|
467
|
+
expect((0, store_1.readJournal)(repo, 'main').state).toHaveProperty('admissionRetry.attempts', 1);
|
|
468
|
+
now += 5000;
|
|
469
|
+
verdict = 'current';
|
|
470
|
+
expect(await restarted.tick()).toBe('continue');
|
|
471
|
+
expect((0, store_1.readJournal)(repo, 'main').state).not.toHaveProperty('admissionRetry');
|
|
472
|
+
expect((0, store_1.readJournal)(repo, 'main').state.cycle.status).toBe('IN_PROGRESS');
|
|
473
|
+
expect((0, store_1.readJournal)(repo, 'main').state.generations).toHaveLength(1);
|
|
474
|
+
expect(spawned).toBeGreaterThan(0);
|
|
475
|
+
});
|
|
476
|
+
test('stale currentness is conclusive and never receives transient retry budget', async () => {
|
|
477
|
+
initUnattendedFixture(repo);
|
|
478
|
+
const blocked = async () => ({ state: 'blocked', planState: 'valid', executionMode: 'desatendido', journal: 'current', currentness: 'stale', sensors: 'not-required', diagnostics: [{ code: 'ADMISSION_CURRENTNESS_BLOCKED', message: 'stale' }] });
|
|
479
|
+
let spawned = 0;
|
|
480
|
+
expect(await new supervisor_1.Supervisor(repo, 'main', supervisor_1.DEFAULT_SUPERVISOR_CONFIG, () => { spawned++; }, undefined, blocked).tick()).toBe('custody');
|
|
481
|
+
const state = (0, store_1.readJournal)(repo, 'main').state;
|
|
482
|
+
expect(state.cycle.status).toBe('BLOCKED');
|
|
483
|
+
expect(state).not.toHaveProperty('admissionRetry');
|
|
484
|
+
expect(state.generations).toHaveLength(0);
|
|
485
|
+
expect(spawned).toBe(0);
|
|
486
|
+
});
|
|
487
|
+
test('first admission observation binds provider, runtime and autonomy; changed restart identity never dispatches', async () => {
|
|
488
|
+
initUnattendedFixture(repo);
|
|
489
|
+
const identity = { kind: 'native', version: '1.0.0', accountScopeDigest: 'a'.repeat(64) };
|
|
490
|
+
const cfg = { ...supervisor_1.DEFAULT_SUPERVISOR_CONFIG, routingIdentity: identity, controllerAutonomy: 'approval-free' };
|
|
491
|
+
const notCurrent = async () => ({ state: 'blocked', planState: 'valid', executionMode: 'desatendido', journal: 'not-required', currentness: 'unverifiable', sensors: 'not-required', diagnostics: [{ code: 'ADMISSION_CURRENTNESS_BLOCKED', message: 'temporary' }] });
|
|
492
|
+
let spawns = 0;
|
|
493
|
+
expect(await new supervisor_1.Supervisor(repo, 'main', cfg, () => { spawns++; }, undefined, notCurrent).tick()).toBe('continue');
|
|
494
|
+
expect((0, store_1.readJournal)(repo, 'main').state).toHaveProperty('admissionContext', { provider: 'codex', runtime: identity, controllerAutonomy: 'approval-free' });
|
|
495
|
+
const changed = { ...cfg, routingIdentity: { ...identity, accountScopeDigest: 'b'.repeat(64) } };
|
|
496
|
+
expect(await new supervisor_1.Supervisor(repo, 'main', changed, () => { spawns++; }, undefined, admitted).tick()).toBe('custody');
|
|
497
|
+
expect((0, store_1.readJournal)(repo, 'main').state.cycle.status).toBe('BLOCKED');
|
|
498
|
+
expect(spawns).toBe(0);
|
|
499
|
+
});
|
|
500
|
+
test('alternating transient causes share one durable retry budget', async () => {
|
|
501
|
+
initUnattendedFixture(repo);
|
|
502
|
+
let now = Date.now();
|
|
503
|
+
jest.spyOn(Date, 'now').mockImplementation(() => now);
|
|
504
|
+
let reads = 0;
|
|
505
|
+
const alternating = async () => {
|
|
506
|
+
const sensor = reads++ % 2 === 1;
|
|
507
|
+
return sensor
|
|
508
|
+
? { state: 'blocked', planState: 'valid', executionMode: 'desatendido', journal: 'not-required', currentness: 'current', sensors: 'not-certified', diagnostics: [{ code: 'ADMISSION_SENSORS_BLOCKED', message: 'temporary' }] }
|
|
509
|
+
: { state: 'blocked', planState: 'valid', executionMode: 'desatendido', journal: 'not-required', currentness: 'unverifiable', sensors: 'not-required', diagnostics: [{ code: 'ADMISSION_CURRENTNESS_BLOCKED', message: 'temporary' }] };
|
|
510
|
+
};
|
|
511
|
+
let spawns = 0;
|
|
512
|
+
for (let i = 0; i < supervisor_1.MAX_SENSOR_DEFERRALS + 1; i++) {
|
|
513
|
+
const outcome = await new supervisor_1.Supervisor(repo, 'main', supervisor_1.DEFAULT_SUPERVISOR_CONFIG, () => { spawns++; }, undefined, alternating).tick();
|
|
514
|
+
expect(outcome).toBe(i < supervisor_1.MAX_SENSOR_DEFERRALS ? 'continue' : 'custody');
|
|
515
|
+
now += 5000;
|
|
516
|
+
}
|
|
517
|
+
expect(reads).toBe(supervisor_1.MAX_SENSOR_DEFERRALS + 1);
|
|
518
|
+
expect(spawns).toBe(0);
|
|
519
|
+
expect((0, store_1.readJournal)(repo, 'main').state.generations).toHaveLength(0);
|
|
520
|
+
});
|
|
521
|
+
test('an incomplete first configuration can be corrected before dispatch without trapping the cycle', async () => {
|
|
522
|
+
initUnattendedFixture(repo);
|
|
523
|
+
const notCurrent = async () => ({ state: 'blocked', planState: 'valid', executionMode: 'desatendido', journal: 'not-required', currentness: 'unverifiable', sensors: 'not-required', diagnostics: [{ code: 'ADMISSION_CURRENTNESS_BLOCKED', message: 'temporary' }] });
|
|
524
|
+
let spawned = 0;
|
|
525
|
+
expect(await new supervisor_1.Supervisor(repo, 'main', supervisor_1.DEFAULT_SUPERVISOR_CONFIG, () => { spawned++; }, undefined, notCurrent).tick()).toBe('continue');
|
|
526
|
+
expect((0, store_1.readJournal)(repo, 'main').state).not.toHaveProperty('admissionContext');
|
|
527
|
+
const cfg = { ...supervisor_1.DEFAULT_SUPERVISOR_CONFIG, routingIdentity: { kind: 'native', version: '1.0.0', accountScopeDigest: 'a'.repeat(64) }, controllerAutonomy: 'approval-free' };
|
|
528
|
+
const retry = (0, store_1.readJournal)(repo, 'main').state.admissionRetry;
|
|
529
|
+
jest.spyOn(Date, 'now').mockReturnValue(Date.parse(retry.nextRetryAt));
|
|
530
|
+
expect(await new supervisor_1.Supervisor(repo, 'main', cfg, () => { spawned++; }, undefined, admitted, true).tick()).toBe('continue');
|
|
531
|
+
expect((0, store_1.readJournal)(repo, 'main').state).toHaveProperty('admissionContext', { provider: 'codex', runtime: cfg.routingIdentity, controllerAutonomy: 'approval-free' });
|
|
532
|
+
expect(spawned).toBeGreaterThan(0);
|
|
533
|
+
});
|
|
446
534
|
test('una admisión compacta bloqueada no lanza controlador ni wrappers', async () => {
|
|
447
535
|
initUnattendedFixture(repo);
|
|
448
536
|
const state = (0, store_1.readJournal)(repo, 'main').state;
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|