sneakoscope 1.18.1 → 1.18.3

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 (61) hide show
  1. package/README.md +6 -4
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/build-manifest.json +15 -9
  8. package/dist/commands/image-ux-review.d.ts +51 -2
  9. package/dist/commands/ppt.d.ts +51 -2
  10. package/dist/core/agents/agent-command-surface.d.ts +4 -0
  11. package/dist/core/agents/agent-command-surface.js +6 -2
  12. package/dist/core/agents/agent-follow-up-work-items.d.ts +20 -0
  13. package/dist/core/agents/agent-follow-up-work-items.js +115 -0
  14. package/dist/core/agents/agent-janitor.d.ts +3 -0
  15. package/dist/core/agents/agent-janitor.js +7 -1
  16. package/dist/core/agents/agent-orchestrator.d.ts +51 -2
  17. package/dist/core/agents/agent-orchestrator.js +104 -9
  18. package/dist/core/agents/agent-output-validator.d.ts +51 -0
  19. package/dist/core/agents/agent-output-validator.js +34 -0
  20. package/dist/core/agents/agent-proof-evidence.d.ts +32 -1
  21. package/dist/core/agents/agent-proof-evidence.js +77 -3
  22. package/dist/core/agents/agent-runner-codex-exec.d.ts +1 -0
  23. package/dist/core/agents/agent-runner-fake.js +20 -0
  24. package/dist/core/agents/agent-runner-process.js +16 -1
  25. package/dist/core/agents/agent-runner-tmux.d.ts +1 -1
  26. package/dist/core/agents/agent-runner-tmux.js +44 -7
  27. package/dist/core/agents/agent-scheduler.d.ts +2 -0
  28. package/dist/core/agents/agent-scheduler.js +20 -2
  29. package/dist/core/agents/agent-schema.d.ts +18 -0
  30. package/dist/core/agents/agent-task-graph.d.ts +52 -0
  31. package/dist/core/agents/agent-task-graph.js +193 -0
  32. package/dist/core/agents/agent-terminal-session.d.ts +11 -0
  33. package/dist/core/agents/agent-terminal-session.js +48 -0
  34. package/dist/core/agents/agent-trust-report.d.ts +12 -0
  35. package/dist/core/agents/agent-trust-report.js +21 -0
  36. package/dist/core/agents/agent-work-partition.d.ts +15 -1
  37. package/dist/core/agents/agent-work-partition.js +23 -2
  38. package/dist/core/agents/agent-work-queue.js +11 -5
  39. package/dist/core/agents/agent-worker-pipeline.js +7 -1
  40. package/dist/core/agents/agent-wrongness.d.ts +1 -1
  41. package/dist/core/agents/agent-wrongness.js +2 -0
  42. package/dist/core/agents/route-collaboration-ledger.d.ts +51 -2
  43. package/dist/core/agents/tmux-lane-supervisor.d.ts +54 -0
  44. package/dist/core/agents/tmux-lane-supervisor.js +253 -0
  45. package/dist/core/agents/work-partition/task-slicer.d.ts +4 -0
  46. package/dist/core/agents/work-partition/task-slicer.js +18 -1
  47. package/dist/core/commands/agent-command.js +10 -3
  48. package/dist/core/commands/command-utils.js +2 -1
  49. package/dist/core/commands/image-ux-review-command.d.ts +51 -2
  50. package/dist/core/commands/ppt-command.d.ts +51 -2
  51. package/dist/core/commands/qa-loop-command.js +9 -3
  52. package/dist/core/commands/research-command.js +7 -2
  53. package/dist/core/commands/team-command.js +17 -3
  54. package/dist/core/fsx.d.ts +1 -1
  55. package/dist/core/fsx.js +1 -1
  56. package/dist/core/routes.js +2 -2
  57. package/dist/core/team-live.js +6 -0
  58. package/dist/core/version.d.ts +1 -1
  59. package/dist/core/version.js +1 -1
  60. package/package.json +20 -2
  61. package/schemas/codex/agent-result.schema.json +30 -0
package/README.md CHANGED
@@ -10,7 +10,7 @@ SKS does not try to clone every other harness. It focuses on one thing: making C
10
10
 
11
11
  ## Current Release
12
12
 
13
- SKS **1.18.1** adds Dynamic Agent Pool replenishment: `agents=5` now means keep up to five active worker slots running until the work queue drains, with immutable session generations, generation-aware terminal close reports, real/fake-tmux pane launch evidence, and Source Intelligence / Goal mode refs propagated to every generation.
13
+ SKS **1.18.3** closes the route-truth Dynamic Worker Pool runtime: `agents=5` means five target active worker slots, while `--work-items N` controls the route work queue. Pending work backfills empty slots immediately, tmux lanes persist as worker-slot UI until scheduler drain, follow-up work items are schema-bound, and Agent/Team/Research/QA blackboxes now execute the actual route commands while proving task graph, work queue, scheduler, terminal, Source Intelligence, Goal, and tmux supervisor evidence.
14
14
 
15
15
  ```bash
16
16
  sks mad-sks plan --target-root <path> --json
@@ -19,10 +19,12 @@ sks mad-sks proof --json
19
19
  sks mad-sks rollback-apply --rollback-plan <path> --yes --json
20
20
  sks features complete --json
21
21
  sks agent status latest --json
22
- sks agent run "release review" --agents 8 --concurrency 4 --mock --json
22
+ sks agent run "release review" --agents 8 --work-items 16 --concurrency 4 --mock --json
23
23
  npm run source-intelligence:all-modes
24
24
  npm run agent:background-terminals
25
- npm run agent:tmux-right-lanes
25
+ npm run agent:tmux-lane-no-flicker
26
+ npm run agent:backfill-route-blackbox
27
+ npm run team:actual-route-backfill
26
28
  npm run release:readiness
27
29
  ```
28
30
 
@@ -612,7 +614,7 @@ npm run release:check
612
614
  npm run publish:dry
613
615
  ```
614
616
 
615
- `release:check` runs the 1.18.1 dynamic agent pool closure DAG, writes a source digest stamp under `.sneakoscope/reports/`, then refreshes release readiness so publish commands can verify the same stamp. The DAG preserves the 1.18 baseline gates and adds dynamic pool, backfill replenishment, scheduler proof, session generation, terminal generation, tmux real right-lane, dynamic cockpit, Source Intelligence propagation, and Goal mode propagation checks. Broader live or historical gates remain explicit scripts such as `release:real-check`. Generate the human-readable registry with `sks features inventory --write-docs`. Plain `npm publish` uses the `latest` dist-tag. npm's `prepublishOnly` verifies the fresh release stamp instead of rerunning the full gate, and `prepack` only rebuilds `dist`; publish no longer repeats the expensive release suite during packaging. `npm run publish:dry` remains the explicit dry-run helper.
617
+ `release:check` runs the 1.18.3 route-truth dynamic scheduler closure DAG, writes a source digest stamp under `.sneakoscope/reports/`, then refreshes release readiness so publish commands can verify the same stamp. The DAG preserves the 1.18 baseline gates and adds task graph expansion, schema-bound follow-up work, actual Agent/Team/Research/QA route backfill blackboxes, scheduler proof hardening, persistent tmux lane supervisor integration, no-flicker lane proof, session generation, terminal generation, dynamic cockpit, Source Intelligence propagation, and Goal mode propagation checks. Broader live or historical gates remain explicit scripts such as `release:real-check`. Generate the human-readable registry with `sks features inventory --write-docs`. Plain `npm publish` uses the `latest` dist-tag. npm's `prepublishOnly` verifies the fresh release stamp instead of rerunning the full gate, and `prepack` only rebuilds `dist`; publish no longer repeats the expensive release suite during packaging. `npm run publish:dry` remains the explicit dry-run helper.
616
618
 
617
619
  Version bumps are manual. Run `sks versioning bump` only when preparing release metadata; SKS will not create `.git/hooks/pre-commit` or auto-bump during ordinary commits.
618
620
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "1.18.1"
79
+ version = "1.18.3"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "1.18.1"
3
+ version = "1.18.3"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
4
4
  fn main() {
5
5
  let mut args = std::env::args().skip(1);
6
6
  match args.next().as_deref() {
7
- Some("--version") => println!("sks-rs 1.18.1"),
7
+ Some("--version") => println!("sks-rs 1.18.3"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schema": "sks.dist-build-stamp.v1",
3
3
  "package_name": "sneakoscope",
4
- "package_version": "1.18.1",
5
- "source_digest": "6df40e82722a56a4df2efafa956011204d7a96fd218b43d19aa10672bbd0842c",
6
- "source_file_count": 1312,
7
- "built_at_source_time": 1779722031788
4
+ "package_version": "1.18.3",
5
+ "source_digest": "5aefcff7c0119b0c22dc3ca2d50edd2dbd5f069ea1a9838cee14700f0ede28de",
6
+ "source_file_count": 1370,
7
+ "built_at_source_time": 1779767703495
8
8
  }
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '1.18.1';
2
+ const FAST_PACKAGE_VERSION = '1.18.3';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--version' || args[0] === '-v' || args[0] === 'version') {
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "schema": "sks.dist-build.v2",
3
- "version": "1.18.1",
4
- "package_version": "1.18.1",
3
+ "version": "1.18.3",
4
+ "package_version": "1.18.3",
5
5
  "typescript": true,
6
6
  "mjs_runtime_files": 0,
7
- "compiled_file_count": 858,
8
- "compiled_js_count": 429,
9
- "compiled_dts_count": 429,
10
- "source_digest": "6df40e82722a56a4df2efafa956011204d7a96fd218b43d19aa10672bbd0842c",
11
- "source_file_count": 1312,
12
- "source_files_hash": "fbe2aa994acefbdab214a17074d539960fa99369b928c3679442019ace20e7c6",
13
- "source_list_hash": "fbe2aa994acefbdab214a17074d539960fa99369b928c3679442019ace20e7c6",
7
+ "compiled_file_count": 864,
8
+ "compiled_js_count": 432,
9
+ "compiled_dts_count": 432,
10
+ "source_digest": "5aefcff7c0119b0c22dc3ca2d50edd2dbd5f069ea1a9838cee14700f0ede28de",
11
+ "source_file_count": 1370,
12
+ "source_files_hash": "1807c675876cb62160e1f8e2fb69a846610ba2a34fa01154263805337a98c149",
13
+ "source_list_hash": "1807c675876cb62160e1f8e2fb69a846610ba2a34fa01154263805337a98c149",
14
14
  "src_mjs_runtime_files": 0,
15
15
  "dist_stamp_schema": "sks.dist-build-stamp.v1",
16
16
  "files": [
@@ -198,6 +198,8 @@
198
198
  "core/agents/agent-consensus.js",
199
199
  "core/agents/agent-effort-policy.d.ts",
200
200
  "core/agents/agent-effort-policy.js",
201
+ "core/agents/agent-follow-up-work-items.d.ts",
202
+ "core/agents/agent-follow-up-work-items.js",
201
203
  "core/agents/agent-gate.d.ts",
202
204
  "core/agents/agent-gate.js",
203
205
  "core/agents/agent-heartbeat.d.ts",
@@ -242,6 +244,8 @@
242
244
  "core/agents/agent-session-generation.js",
243
245
  "core/agents/agent-session-rows.d.ts",
244
246
  "core/agents/agent-session-rows.js",
247
+ "core/agents/agent-task-graph.d.ts",
248
+ "core/agents/agent-task-graph.js",
245
249
  "core/agents/agent-task-slicer.d.ts",
246
250
  "core/agents/agent-task-slicer.js",
247
251
  "core/agents/agent-terminal-session.d.ts",
@@ -262,6 +266,8 @@
262
266
  "core/agents/route-collaboration-ledger.js",
263
267
  "core/agents/scout-policy.d.ts",
264
268
  "core/agents/scout-policy.js",
269
+ "core/agents/tmux-lane-supervisor.d.ts",
270
+ "core/agents/tmux-lane-supervisor.js",
265
271
  "core/agents/tmux-right-lane-cockpit.d.ts",
266
272
  "core/agents/tmux-right-lane-cockpit.js",
267
273
  "core/agents/work-partition/conflict-detector.d.ts",
@@ -385,6 +385,8 @@ export declare function run(command: any, args?: any): Promise<void | {
385
385
  ok: boolean;
386
386
  mission_id: any;
387
387
  route: string;
388
+ route_command: string;
389
+ route_blackbox_kind: string;
388
390
  backend: "tmux" | "fake" | "process" | "codex-exec";
389
391
  ledger_root: string;
390
392
  roster: {
@@ -436,6 +438,16 @@ export declare function run(command: any, args?: any): Promise<void | {
436
438
  lease_count: number;
437
439
  blockers: string[];
438
440
  };
441
+ task_graph: {
442
+ target_active_slots: number;
443
+ total_work_items: number;
444
+ generated_from_route: string;
445
+ work_items_exceed_active_slots: boolean;
446
+ };
447
+ requested_work_items: number;
448
+ actual_total_work_items: number;
449
+ target_active_slots: number;
450
+ minimum_work_items: number;
439
451
  scheduler: {
440
452
  schema: string;
441
453
  ok: boolean;
@@ -549,12 +561,24 @@ export declare function run(command: any, args?: any): Promise<void | {
549
561
  terminal_sessions_closed: any;
550
562
  terminal_close_report: string;
551
563
  target_active_slots: any;
564
+ total_work_items: any;
565
+ pending_count: any;
566
+ active_slot_count: any;
567
+ completed_count: any;
552
568
  max_observed_active_slots: any;
553
569
  backfill_count: any;
570
+ expected_backfill_count: any;
554
571
  pending_queue_drained: any;
555
572
  generation_count: any;
556
573
  tmux_attach_command: string | null;
557
574
  tmux_lane_manifest: string;
575
+ tmux_lane_persistence: {
576
+ supervisor: string;
577
+ no_flicker_verified: boolean;
578
+ pane_survival_checked: boolean;
579
+ unexpected_close_count: number;
580
+ lane_count: number;
581
+ };
558
582
  output_schema_ok: boolean;
559
583
  output_tail_report: string;
560
584
  output_tail_records: number;
@@ -569,7 +593,7 @@ export declare function run(command: any, args?: any): Promise<void | {
569
593
  generated_at: string;
570
594
  records: {
571
595
  schema: string;
572
- kind: "xai_available_not_used" | "context7_missing" | "codex_web_search_missing" | "recursion_attempt" | "lease_conflict" | "session_not_closed" | "terminal_missing" | "terminal_not_closed" | "scheduler_starvation" | "session_generation_missing" | "schema_invalid_output" | "stale_heartbeat" | "legacy_multiagent_runtime_usage_attempt";
596
+ kind: "xai_available_not_used" | "context7_missing" | "codex_web_search_missing" | "recursion_attempt" | "lease_conflict" | "session_not_closed" | "terminal_missing" | "terminal_not_closed" | "scheduler_starvation" | "tmux_lane_flicker" | "missing_follow_up_schema" | "session_generation_missing" | "schema_invalid_output" | "stale_heartbeat" | "legacy_multiagent_runtime_usage_attempt";
573
597
  blocker: string;
574
598
  created_at: string;
575
599
  status: string;
@@ -582,6 +606,10 @@ export declare function run(command: any, args?: any): Promise<void | {
582
606
  generated_at: string;
583
607
  mission_id: string;
584
608
  backend: string;
609
+ route: string;
610
+ route_command: string;
611
+ route_blackbox_kind: string;
612
+ real_route_command_used: boolean;
585
613
  real_parallel_claim: boolean;
586
614
  fake_backend_disclaimer: string | null;
587
615
  agent_count: any;
@@ -597,21 +625,42 @@ export declare function run(command: any, args?: any): Promise<void | {
597
625
  session_generation_count: number;
598
626
  all_generations_closed: boolean;
599
627
  scheduler_state: string;
600
- target_active_slots: any;
628
+ target_active_slots: number;
629
+ requested_work_items: number;
630
+ actual_total_work_items: number;
631
+ minimum_work_items: number;
632
+ task_graph_total_work_items: number;
633
+ work_queue_total_work_items: number;
634
+ scheduler_total_work_items: number;
635
+ task_graph_matches_cli_options: boolean;
636
+ work_queue_matches_task_graph: boolean;
637
+ scheduler_matches_work_queue: boolean;
601
638
  max_observed_active_slots: any;
602
639
  pending_queue_drained: boolean;
603
640
  backfill_count: any;
604
641
  expected_backfill_count: any;
642
+ backfill_expected_for_route: boolean;
605
643
  slot_count: any;
606
644
  generation_count: number;
607
645
  all_slots_closed_after_drain: boolean;
608
646
  generated_work_item_count: any;
609
647
  source_intelligence_generation_refs_ok: boolean;
610
648
  goal_mode_generation_refs_ok: boolean;
649
+ task_graph_source_refs_ok: any;
650
+ task_graph_goal_refs_ok: any;
651
+ work_queue_source_refs_ok: any;
652
+ work_queue_goal_refs_ok: any;
611
653
  tmux_lane_manifest: string;
612
654
  tmux_lane_manifest_ok: boolean;
655
+ tmux_lane_supervisor: string;
656
+ lane_supervisor_integrated: boolean;
657
+ tmux_lane_no_flicker_verified: boolean;
658
+ tmux_lane_survival_checked: boolean;
659
+ tmux_lane_unexpected_close_count: number;
660
+ tmux_lane_auto_reopen_count: number;
613
661
  tmux_pane_launch_ledger: string;
614
662
  tmux_pane_launch_count: number;
663
+ terminal_reports_match_generations: boolean;
615
664
  ledger_hash_chain_ok: boolean;
616
665
  no_overlap_ok: boolean;
617
666
  consensus_ok: boolean;
@@ -237,6 +237,8 @@ export declare function run(command: any, args?: any): Promise<void | {
237
237
  ok: boolean;
238
238
  mission_id: any;
239
239
  route: string;
240
+ route_command: string;
241
+ route_blackbox_kind: string;
240
242
  backend: "tmux" | "fake" | "process" | "codex-exec";
241
243
  ledger_root: string;
242
244
  roster: {
@@ -288,6 +290,16 @@ export declare function run(command: any, args?: any): Promise<void | {
288
290
  lease_count: number;
289
291
  blockers: string[];
290
292
  };
293
+ task_graph: {
294
+ target_active_slots: number;
295
+ total_work_items: number;
296
+ generated_from_route: string;
297
+ work_items_exceed_active_slots: boolean;
298
+ };
299
+ requested_work_items: number;
300
+ actual_total_work_items: number;
301
+ target_active_slots: number;
302
+ minimum_work_items: number;
291
303
  scheduler: {
292
304
  schema: string;
293
305
  ok: boolean;
@@ -401,12 +413,24 @@ export declare function run(command: any, args?: any): Promise<void | {
401
413
  terminal_sessions_closed: any;
402
414
  terminal_close_report: string;
403
415
  target_active_slots: any;
416
+ total_work_items: any;
417
+ pending_count: any;
418
+ active_slot_count: any;
419
+ completed_count: any;
404
420
  max_observed_active_slots: any;
405
421
  backfill_count: any;
422
+ expected_backfill_count: any;
406
423
  pending_queue_drained: any;
407
424
  generation_count: any;
408
425
  tmux_attach_command: string | null;
409
426
  tmux_lane_manifest: string;
427
+ tmux_lane_persistence: {
428
+ supervisor: string;
429
+ no_flicker_verified: boolean;
430
+ pane_survival_checked: boolean;
431
+ unexpected_close_count: number;
432
+ lane_count: number;
433
+ };
410
434
  output_schema_ok: boolean;
411
435
  output_tail_report: string;
412
436
  output_tail_records: number;
@@ -421,7 +445,7 @@ export declare function run(command: any, args?: any): Promise<void | {
421
445
  generated_at: string;
422
446
  records: {
423
447
  schema: string;
424
- kind: "xai_available_not_used" | "context7_missing" | "codex_web_search_missing" | "recursion_attempt" | "lease_conflict" | "session_not_closed" | "terminal_missing" | "terminal_not_closed" | "scheduler_starvation" | "session_generation_missing" | "schema_invalid_output" | "stale_heartbeat" | "legacy_multiagent_runtime_usage_attempt";
448
+ kind: "xai_available_not_used" | "context7_missing" | "codex_web_search_missing" | "recursion_attempt" | "lease_conflict" | "session_not_closed" | "terminal_missing" | "terminal_not_closed" | "scheduler_starvation" | "tmux_lane_flicker" | "missing_follow_up_schema" | "session_generation_missing" | "schema_invalid_output" | "stale_heartbeat" | "legacy_multiagent_runtime_usage_attempt";
425
449
  blocker: string;
426
450
  created_at: string;
427
451
  status: string;
@@ -434,6 +458,10 @@ export declare function run(command: any, args?: any): Promise<void | {
434
458
  generated_at: string;
435
459
  mission_id: string;
436
460
  backend: string;
461
+ route: string;
462
+ route_command: string;
463
+ route_blackbox_kind: string;
464
+ real_route_command_used: boolean;
437
465
  real_parallel_claim: boolean;
438
466
  fake_backend_disclaimer: string | null;
439
467
  agent_count: any;
@@ -449,21 +477,42 @@ export declare function run(command: any, args?: any): Promise<void | {
449
477
  session_generation_count: number;
450
478
  all_generations_closed: boolean;
451
479
  scheduler_state: string;
452
- target_active_slots: any;
480
+ target_active_slots: number;
481
+ requested_work_items: number;
482
+ actual_total_work_items: number;
483
+ minimum_work_items: number;
484
+ task_graph_total_work_items: number;
485
+ work_queue_total_work_items: number;
486
+ scheduler_total_work_items: number;
487
+ task_graph_matches_cli_options: boolean;
488
+ work_queue_matches_task_graph: boolean;
489
+ scheduler_matches_work_queue: boolean;
453
490
  max_observed_active_slots: any;
454
491
  pending_queue_drained: boolean;
455
492
  backfill_count: any;
456
493
  expected_backfill_count: any;
494
+ backfill_expected_for_route: boolean;
457
495
  slot_count: any;
458
496
  generation_count: number;
459
497
  all_slots_closed_after_drain: boolean;
460
498
  generated_work_item_count: any;
461
499
  source_intelligence_generation_refs_ok: boolean;
462
500
  goal_mode_generation_refs_ok: boolean;
501
+ task_graph_source_refs_ok: any;
502
+ task_graph_goal_refs_ok: any;
503
+ work_queue_source_refs_ok: any;
504
+ work_queue_goal_refs_ok: any;
463
505
  tmux_lane_manifest: string;
464
506
  tmux_lane_manifest_ok: boolean;
507
+ tmux_lane_supervisor: string;
508
+ lane_supervisor_integrated: boolean;
509
+ tmux_lane_no_flicker_verified: boolean;
510
+ tmux_lane_survival_checked: boolean;
511
+ tmux_lane_unexpected_close_count: number;
512
+ tmux_lane_auto_reopen_count: number;
465
513
  tmux_pane_launch_ledger: string;
466
514
  tmux_pane_launch_count: number;
515
+ terminal_reports_match_generations: boolean;
467
516
  ledger_hash_chain_ok: boolean;
468
517
  no_overlap_ok: boolean;
469
518
  consensus_ok: boolean;
@@ -4,6 +4,10 @@ export declare function parseAgentCommandArgs(command: string, args?: string[]):
4
4
  prompt: string;
5
5
  route: string;
6
6
  agents: number;
7
+ targetActiveSlots: number;
8
+ desiredWorkItemCount: number;
9
+ minimumWorkItems: number;
10
+ maxQueueExpansion: number;
7
11
  concurrency: number;
8
12
  backend: string;
9
13
  mock: boolean;
@@ -6,6 +6,10 @@ export function parseAgentCommandArgs(command, args = []) {
6
6
  const rest = action === first ? args.slice(1) : args;
7
7
  const json = hasFlag(args, '--json');
8
8
  const agents = Number(readOption(args, '--agents', DEFAULT_AGENT_COUNT));
9
+ const targetActiveSlots = Number(readOption(args, '--target-active-slots', agents));
10
+ const desiredWorkItemCount = Number(readOption(args, '--work-items', targetActiveSlots));
11
+ const minimumWorkItems = Number(readOption(args, '--minimum-work-items', targetActiveSlots));
12
+ const maxQueueExpansion = Number(readOption(args, '--max-queue-expansion', 10));
9
13
  const concurrency = Number(readOption(args, '--concurrency', Math.min(agents, 5)));
10
14
  const backend = String(readOption(args, '--backend', hasFlag(args, '--mock') ? 'fake' : 'codex-exec'));
11
15
  const route = String(readOption(args, '--route', '$Agent'));
@@ -13,14 +17,14 @@ export function parseAgentCommandArgs(command, args = []) {
13
17
  const real = hasFlag(args, '--real');
14
18
  const readonly = hasFlag(args, '--readonly') || hasFlag(args, '--read-only');
15
19
  const codexApp = hasFlag(args, '--codex-app');
16
- const positionals = positionalArgs(rest, new Set(['--agents', '--concurrency', '--backend', '--route', '--mission', '--mission-id', '--agent', '--lane']));
20
+ const positionals = positionalArgs(rest, new Set(['--agents', '--target-active-slots', '--work-items', '--minimum-work-items', '--max-queue-expansion', '--concurrency', '--backend', '--route', '--mission', '--mission-id', '--agent', '--lane']));
17
21
  const missionDefault = action === 'run' || action === 'spawn' || action === 'plan' ? '' : 'latest';
18
22
  const positionalMission = action === 'run' || action === 'spawn' || action === 'plan' ? '' : (positionals[0] || '');
19
23
  const missionId = String(readOption(args, '--mission', readOption(args, '--mission-id', positionalMission || missionDefault)));
20
24
  const lane = String(readOption(args, '--agent', readOption(args, '--lane', '')));
21
25
  const promptPositionals = positionalMission ? positionals.slice(1) : positionals;
22
26
  const prompt = promptPositionals.join(' ').trim() || 'Native agent run';
23
- return { command, action, prompt, route, agents, concurrency, backend, mock, real, readonly, json, missionId, lane, codexApp };
27
+ return { command, action, prompt, route, agents, targetActiveSlots, desiredWorkItemCount, minimumWorkItems, maxQueueExpansion, concurrency, backend, mock, real, readonly, json, missionId, lane, codexApp };
24
28
  }
25
29
  function hasFlag(args, flag) {
26
30
  return args.includes(flag);
@@ -0,0 +1,20 @@
1
+ export interface AgentFollowUpWorkItem {
2
+ id: string;
3
+ title: string;
4
+ description: string;
5
+ required_persona_category: string;
6
+ priority: number;
7
+ dependencies: string[];
8
+ lease_requirements: unknown[];
9
+ max_attempts: number;
10
+ reason: string;
11
+ source_agent_session_id?: string;
12
+ }
13
+ export interface AgentFollowUpValidationResult {
14
+ accepted: AgentFollowUpWorkItem[];
15
+ blockers: string[];
16
+ }
17
+ export declare function normalizeAgentFollowUpWorkItems(rawItems: unknown, input?: {
18
+ originSessionId?: string | null;
19
+ }): AgentFollowUpValidationResult;
20
+ //# sourceMappingURL=agent-follow-up-work-items.d.ts.map
@@ -0,0 +1,115 @@
1
+ import { scanAgentTextForRecursion } from './agent-recursion-guard.js';
2
+ const FOLLOW_UP_KEYS = new Set([
3
+ 'id',
4
+ 'title',
5
+ 'description',
6
+ 'required_persona_category',
7
+ 'priority',
8
+ 'dependencies',
9
+ 'lease_requirements',
10
+ 'max_attempts',
11
+ 'reason',
12
+ 'source_agent_session_id'
13
+ ]);
14
+ const PROTECTED_WRITE_PATH_RE = /^(?:\.codex|\.agents|AGENTS\.md|node_modules\/sneakoscope|\.sneakoscope\/.*policy.*\.json)(?:\/|$)/;
15
+ const MAIN_ROUTE_RECURSION_RE = /(?:\bsks\s+(?:team|agent|research|qa-loop|goal)\b|\$(?:Team|Goal|Research|QA-LOOP|Agent)\b|main\s+route\s+recursion)/i;
16
+ const GLOBAL_SCOUT_LEDGER_RE = /global\s+scout\s+ledger/i;
17
+ export function normalizeAgentFollowUpWorkItems(rawItems, input = {}) {
18
+ if (rawItems === undefined || rawItems === null)
19
+ return { accepted: [], blockers: [] };
20
+ if (!Array.isArray(rawItems))
21
+ return { accepted: [], blockers: ['follow_up_work_items_not_array'] };
22
+ const accepted = [];
23
+ const blockers = [];
24
+ rawItems.forEach((raw, index) => {
25
+ const result = normalizeAgentFollowUpWorkItem(raw, index, input.originSessionId || null);
26
+ if (result.item)
27
+ accepted.push(result.item);
28
+ blockers.push(...result.blockers);
29
+ });
30
+ return { accepted, blockers };
31
+ }
32
+ function normalizeAgentFollowUpWorkItem(raw, index, originSessionId) {
33
+ const blockers = [];
34
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
35
+ return { item: null, blockers: [`follow_up_work_item_${index + 1}_not_object`] };
36
+ }
37
+ const row = raw;
38
+ for (const key of Object.keys(row)) {
39
+ if (!FOLLOW_UP_KEYS.has(key))
40
+ blockers.push(`follow_up_work_item_${index + 1}_additional_property:${key}`);
41
+ }
42
+ const id = requiredString(row.id, 'id', index, blockers);
43
+ const title = requiredString(row.title, 'title', index, blockers);
44
+ const description = requiredString(row.description, 'description', index, blockers);
45
+ const requiredPersonaCategory = requiredString(row.required_persona_category, 'required_persona_category', index, blockers);
46
+ const reason = requiredString(row.reason, 'reason', index, blockers);
47
+ const priority = requiredInteger(row.priority, 'priority', index, blockers, 0);
48
+ const maxAttempts = requiredInteger(row.max_attempts, 'max_attempts', index, blockers, 1);
49
+ const dependencies = requiredStringArray(row.dependencies, 'dependencies', index, blockers);
50
+ const leaseRequirements = requiredArray(row.lease_requirements, 'lease_requirements', index, blockers);
51
+ const sourceAgentSessionId = typeof row.source_agent_session_id === 'string' && row.source_agent_session_id.trim()
52
+ ? row.source_agent_session_id.trim()
53
+ : originSessionId || undefined;
54
+ const serialized = JSON.stringify(row);
55
+ const recursion = scanAgentTextForRecursion(serialized);
56
+ if (!recursion.ok || MAIN_ROUTE_RECURSION_RE.test(serialized))
57
+ blockers.push(`follow_up_work_item_${index + 1}_main_route_recursion_blocked`);
58
+ if (GLOBAL_SCOUT_LEDGER_RE.test(serialized))
59
+ blockers.push(`follow_up_work_item_${index + 1}_global_scout_ledger_blocked`);
60
+ for (const lease of leaseRequirements) {
61
+ const path = leasePath(lease);
62
+ if (path && PROTECTED_WRITE_PATH_RE.test(path))
63
+ blockers.push(`follow_up_work_item_${index + 1}_protected_core_target:${path}`);
64
+ }
65
+ if (blockers.length)
66
+ return { item: null, blockers };
67
+ return {
68
+ item: {
69
+ id,
70
+ title,
71
+ description,
72
+ required_persona_category: requiredPersonaCategory,
73
+ priority,
74
+ dependencies,
75
+ lease_requirements: leaseRequirements,
76
+ max_attempts: maxAttempts,
77
+ reason,
78
+ ...(sourceAgentSessionId ? { source_agent_session_id: sourceAgentSessionId } : {})
79
+ },
80
+ blockers: []
81
+ };
82
+ }
83
+ function requiredString(value, field, index, blockers) {
84
+ const text = typeof value === 'string' ? value.trim() : '';
85
+ if (!text)
86
+ blockers.push(`follow_up_work_item_${index + 1}_missing_${field}`);
87
+ return text;
88
+ }
89
+ function requiredInteger(value, field, index, blockers, min) {
90
+ const parsed = Number(value);
91
+ if (!Number.isFinite(parsed) || Math.floor(parsed) !== parsed || parsed < min)
92
+ blockers.push(`follow_up_work_item_${index + 1}_invalid_${field}`);
93
+ return Math.max(min, Number.isFinite(parsed) ? Math.floor(parsed) : min);
94
+ }
95
+ function requiredArray(value, field, index, blockers) {
96
+ if (!Array.isArray(value)) {
97
+ blockers.push(`follow_up_work_item_${index + 1}_missing_${field}`);
98
+ return [];
99
+ }
100
+ return value;
101
+ }
102
+ function requiredStringArray(value, field, index, blockers) {
103
+ const rows = requiredArray(value, field, index, blockers);
104
+ const out = rows.map((entry) => String(entry || '')).filter(Boolean);
105
+ if (out.length !== rows.length)
106
+ blockers.push(`follow_up_work_item_${index + 1}_invalid_${field}`);
107
+ return out;
108
+ }
109
+ function leasePath(lease) {
110
+ if (!lease || typeof lease !== 'object')
111
+ return '';
112
+ const row = lease;
113
+ return String(row.path || row.file || row.target_path || '').replace(/\\/g, '/').replace(/^\.?\//, '');
114
+ }
115
+ //# sourceMappingURL=agent-follow-up-work-items.js.map
@@ -8,6 +8,9 @@ export interface AgentJanitorReport {
8
8
  zombie_process_sessions: string[];
9
9
  stale_tmux_sessions: string[];
10
10
  active_generation_sessions: string[];
11
+ active_generation_count: number;
12
+ cleaned_generation_count: number;
13
+ skipped_active_generations: string[];
11
14
  orphan_generation_dirs: string[];
12
15
  slot_generation_cleanup: string[];
13
16
  orphan_temp_dirs: string[];
@@ -34,7 +34,10 @@ export async function runAgentJanitor(input) {
34
34
  statusBySession.set(String(row.session_id), status);
35
35
  }
36
36
  const zombieProcesses = await detectZombieProcessSessions(agentRoot, statusByAgent, statusBySession);
37
- const staleTmuxSessions = await detectStaleTmuxSessions(agentRoot, staleMs);
37
+ const rawStaleTmuxSessions = await detectStaleTmuxSessions(agentRoot, staleMs);
38
+ const activeGenerationSet = new Set(activeGenerationSessions);
39
+ const staleTmuxSessions = rawStaleTmuxSessions.filter((id) => !activeGenerationSet.has(id));
40
+ const skippedActiveGenerations = rawStaleTmuxSessions.filter((id) => activeGenerationSet.has(id));
38
41
  const orphanGenerationDirs = await detectOrphanGenerationDirs(agentRoot, new Set(generationRows.map((row) => String(row.artifact_dir || ''))));
39
42
  const orphanTempDirs = await scopedExistingPaths(Array.isArray(namespace?.orphan_temp_dirs) ? namespace.orphan_temp_dirs : [], projectHash);
40
43
  const staleLocks = await scopedStaleLockPaths(namespace?.lock_dir ? [namespace.lock_dir] : [], projectHash, staleMs);
@@ -65,6 +68,9 @@ export async function runAgentJanitor(input) {
65
68
  zombie_process_sessions: zombieProcesses,
66
69
  stale_tmux_sessions: staleTmuxSessions,
67
70
  active_generation_sessions: activeGenerationSessions,
71
+ active_generation_count: activeGenerationSessions.length,
72
+ cleaned_generation_count: cleaned.filter((entry) => entry.includes(`${path.sep}sessions${path.sep}`)).length,
73
+ skipped_active_generations: skippedActiveGenerations,
68
74
  orphan_generation_dirs: orphanGenerationDirs,
69
75
  slot_generation_cleanup: cleaned.filter((entry) => entry.includes(`${path.sep}sessions${path.sep}`)),
70
76
  orphan_temp_dirs: orphanTempDirs,