dirigent-core 0.26.0__tar.gz → 0.27.0__tar.gz

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 (76) hide show
  1. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/PKG-INFO +4 -4
  2. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/pyproject.toml +4 -4
  3. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/pyproject.toml.orig +4 -4
  4. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/alembic/versions/0001_baseline_schema.py +16 -165
  5. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/blockdocs.py +6 -6
  6. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/config.py +27 -18
  7. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/directory.py +3 -3
  8. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/documents.py +5 -78
  9. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/__init__.py +0 -2
  10. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/definition.py +8 -49
  11. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/executor.py +12 -41
  12. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/references.py +3 -3
  13. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/runs.py +6 -35
  14. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/services.py +1 -3
  15. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/logging.py +0 -45
  16. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/messages.py +47 -151
  17. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/models.py +35 -120
  18. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/pipelines.py +16 -35
  19. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/plugins.py +32 -21
  20. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/retention.py +1 -7
  21. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/scheduler.py +0 -6
  22. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/sources/endpoint.py +9 -1
  23. dirigent_core-0.27.0/src/dirigent_core/sources/holding.py +357 -0
  24. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/sources/intake.py +170 -35
  25. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/sources/kinds.py +10 -4
  26. dirigent_core-0.27.0/src/dirigent_core/sources/polling.py +452 -0
  27. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/sources/rows.py +194 -24
  28. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/sources/rules.py +9 -17
  29. dirigent_core-0.27.0/src/dirigent_core/sources/sensor.py +164 -0
  30. dirigent_core-0.27.0/src/dirigent_core/sources/storage.py +137 -0
  31. dirigent_core-0.27.0/src/dirigent_core/sources/waiter.py +258 -0
  32. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/telemetry.py +9 -11
  33. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/trigger_documents.py +7 -24
  34. dirigent_core-0.27.0/src/dirigent_core/triggers/__init__.py +69 -0
  35. dirigent_core-0.27.0/src/dirigent_core/triggers/materialize.py +155 -0
  36. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/worker.py +25 -0
  37. dirigent_core-0.26.0/src/dirigent_core/triggers/__init__.py +0 -145
  38. dirigent_core-0.26.0/src/dirigent_core/triggers/materialize.py +0 -291
  39. dirigent_core-0.26.0/src/dirigent_core/triggers/watches.py +0 -640
  40. dirigent_core-0.26.0/src/dirigent_core/triggers/webhooks.py +0 -623
  41. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/LICENSE +0 -0
  42. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/README.md +0 -0
  43. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/__init__.py +0 -0
  44. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/alembic/env.py +0 -0
  45. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/alembic/script.py.mako +0 -0
  46. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/alerting.py +0 -0
  47. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/artifacts.py +0 -0
  48. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/auth.py +0 -0
  49. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/configdocs.py +0 -0
  50. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/database.py +0 -0
  51. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/documentschema.py +0 -0
  52. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/claim.py +0 -0
  53. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/context.py +0 -0
  54. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/expansion.py +0 -0
  55. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/failure.py +0 -0
  56. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/recovery.py +0 -0
  57. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/replies.py +0 -0
  58. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/engine/state.py +0 -0
  59. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/errors.py +0 -0
  60. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/examples.py +0 -0
  61. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/ids.py +0 -0
  62. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/migrations.py +0 -0
  63. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/protocol.py +0 -0
  64. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/py.typed +0 -0
  65. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/ratelimit.py +0 -0
  66. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/registry.py +0 -0
  67. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/reporting.py +0 -0
  68. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/schemas.py +0 -0
  69. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/secrets.py +0 -0
  70. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/sources/__init__.py +0 -0
  71. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/sources/entries.py +0 -0
  72. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/storage.py +0 -0
  73. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/triggers/backfill.py +0 -0
  74. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/triggers/schedules.py +0 -0
  75. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/types.py +0 -0
  76. {dirigent_core-0.26.0 → dirigent_core-0.27.0}/src/dirigent_core/wakeups.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirigent-core
3
- Version: 0.26.0
3
+ Version: 0.27.0
4
4
  Summary: Dirigent engine core: schema, configuration, and plugin host.
5
5
  License-Expression: LicenseRef-Proprietary
6
6
  License-File: LICENSE
@@ -12,9 +12,9 @@ Requires-Dist: argon2-cffi>=25.1.0
12
12
  Requires-Dist: asyncpg>=0.31.0
13
13
  Requires-Dist: cronsim>=2.7
14
14
  Requires-Dist: cryptography>=46.0.5
15
- Requires-Dist: dirigent-client==0.26.0
16
- Requires-Dist: dirigent-common==0.26.0
17
- Requires-Dist: dirigent-plugin==0.26.0
15
+ Requires-Dist: dirigent-client==0.27.0
16
+ Requires-Dist: dirigent-common==0.27.0
17
+ Requires-Dist: dirigent-plugin==0.27.0
18
18
  Requires-Dist: jsonschema>=4.26.0
19
19
  Requires-Dist: opentelemetry-api>=1.44.0
20
20
  Requires-Dist: pydantic-settings>=2.15.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dirigent-core"
3
- version = "0.26.0"
3
+ version = "0.27.0"
4
4
  description = "Dirigent engine core: schema, configuration, and plugin host."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -17,9 +17,9 @@ dependencies = [
17
17
  "asyncpg>=0.31.0",
18
18
  "cronsim>=2.7",
19
19
  "cryptography>=46.0.5",
20
- "dirigent-client==0.26.0",
21
- "dirigent-common==0.26.0",
22
- "dirigent-plugin==0.26.0",
20
+ "dirigent-client==0.27.0",
21
+ "dirigent-common==0.27.0",
22
+ "dirigent-plugin==0.27.0",
23
23
  "jsonschema>=4.26.0",
24
24
  "opentelemetry-api>=1.44.0",
25
25
  "pydantic-settings>=2.15.0",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dirigent-core"
3
- version = "0.26.0"
3
+ version = "0.27.0"
4
4
  description = "Dirigent engine core: schema, configuration, and plugin host."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -17,9 +17,9 @@ dependencies = [
17
17
  "asyncpg>=0.31.0",
18
18
  "cronsim>=2.7",
19
19
  "cryptography>=46.0.5",
20
- "dirigent-client==0.26.0",
21
- "dirigent-common==0.26.0",
22
- "dirigent-plugin==0.26.0",
20
+ "dirigent-client==0.27.0",
21
+ "dirigent-common==0.27.0",
22
+ "dirigent-plugin==0.27.0",
23
23
  "jsonschema>=4.26.0",
24
24
  "opentelemetry-api>=1.44.0",
25
25
  "pydantic-settings>=2.15.0",
@@ -438,113 +438,6 @@ def upgrade() -> None:
438
438
  batch_op.create_index(batch_op.f("ix_schedules_pipeline_id"), ["pipeline_id"], unique=False)
439
439
  batch_op.create_index(batch_op.f("ix_schedules_trigger_document_id"), ["trigger_document_id"], unique=False)
440
440
 
441
- op.create_table(
442
- "webhook_triggers",
443
- sa.Column("id", sa.Uuid(), nullable=False),
444
- sa.Column("pipeline_id", sa.Uuid(), nullable=False),
445
- sa.Column("code", sa.String(length=200), nullable=False),
446
- sa.Column("name", sa.String(length=200), nullable=True),
447
- sa.Column("description", sa.Text(), nullable=True),
448
- sa.Column("token_hash", sa.String(length=128), nullable=False),
449
- sa.Column("token_prefix", sa.String(length=16), server_default="", nullable=False),
450
- sa.Column("hmac_secret", sa.LargeBinary(), nullable=True),
451
- sa.Column("hmac_secret_key_id", sa.String(length=100), nullable=True),
452
- sa.Column(
453
- "params_from_payload",
454
- sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"),
455
- nullable=False,
456
- ),
457
- sa.Column(
458
- "priority",
459
- sa.Enum("low", "normal", "high", name="run_priority", native_enum=False, length=32),
460
- nullable=True,
461
- ),
462
- sa.Column("managed", sa.Boolean(), server_default=sa.false(), nullable=False),
463
- sa.Column("trigger_document_id", sa.Uuid(), nullable=True),
464
- sa.Column("active", sa.Boolean(), server_default=sa.true(), nullable=False),
465
- sa.Column("rate_limit_per_minute", sa.Integer(), server_default=sa.text("60"), nullable=False),
466
- sa.Column("last_delivery_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
467
- sa.Column(
468
- "created_at",
469
- dirigent_core.types.UtcDateTime(timezone=True),
470
- server_default=sa.func.now(),
471
- nullable=False,
472
- ),
473
- sa.Column(
474
- "updated_at",
475
- dirigent_core.types.UtcDateTime(timezone=True),
476
- server_default=sa.func.now(),
477
- nullable=False,
478
- ),
479
- sa.ForeignKeyConstraint(
480
- ["pipeline_id"],
481
- ["pipelines.id"],
482
- name=op.f("fk_webhook_triggers_pipeline_id_pipelines"),
483
- ondelete="CASCADE",
484
- ),
485
- sa.ForeignKeyConstraint(
486
- ["trigger_document_id"],
487
- ["trigger_documents.id"],
488
- name=op.f("fk_webhook_triggers_trigger_document_id_trigger_documents"),
489
- ondelete="CASCADE",
490
- ),
491
- sa.PrimaryKeyConstraint("id", name=op.f("pk_webhook_triggers")),
492
- sa.UniqueConstraint("pipeline_id", "code", name=op.f("uq_webhook_triggers_pipeline_id_code")),
493
- sa.UniqueConstraint("token_hash", name=op.f("uq_webhook_triggers_token_hash")),
494
- )
495
- with op.batch_alter_table("webhook_triggers", schema=None) as batch_op:
496
- batch_op.create_index(batch_op.f("ix_webhook_triggers_pipeline_id"), ["pipeline_id"], unique=False)
497
- batch_op.create_index(
498
- batch_op.f("ix_webhook_triggers_trigger_document_id"), ["trigger_document_id"], unique=False
499
- )
500
-
501
- op.create_table(
502
- "watches",
503
- sa.Column("id", sa.Uuid(), nullable=False),
504
- sa.Column("pipeline_id", sa.Uuid(), nullable=False),
505
- sa.Column("code", sa.String(length=200), nullable=False),
506
- sa.Column("name", sa.String(length=200), nullable=True),
507
- sa.Column("description", sa.Text(), nullable=True),
508
- sa.Column("step", sa.String(length=200), nullable=False),
509
- sa.Column("params", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False),
510
- sa.Column("managed", sa.Boolean(), server_default=sa.false(), nullable=False),
511
- sa.Column("trigger_document_id", sa.Uuid(), nullable=True),
512
- sa.Column("paused", sa.Boolean(), server_default=sa.false(), nullable=False),
513
- sa.Column("cursor", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True),
514
- sa.Column("waiting_run_id", sa.Uuid(), nullable=True),
515
- sa.Column("failures", sa.Integer(), server_default=sa.text("0"), nullable=False),
516
- sa.Column("last_error", sa.Text(), nullable=True),
517
- sa.Column("last_error_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
518
- sa.Column("rearm_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
519
- sa.Column("last_armed_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
520
- sa.Column(
521
- "created_at",
522
- dirigent_core.types.UtcDateTime(timezone=True),
523
- server_default=sa.func.now(),
524
- nullable=False,
525
- ),
526
- sa.Column(
527
- "updated_at",
528
- dirigent_core.types.UtcDateTime(timezone=True),
529
- server_default=sa.func.now(),
530
- nullable=False,
531
- ),
532
- sa.ForeignKeyConstraint(
533
- ["pipeline_id"], ["pipelines.id"], name=op.f("fk_watches_pipeline_id_pipelines"), ondelete="CASCADE"
534
- ),
535
- sa.ForeignKeyConstraint(
536
- ["trigger_document_id"],
537
- ["trigger_documents.id"],
538
- name=op.f("fk_watches_trigger_document_id_trigger_documents"),
539
- ondelete="CASCADE",
540
- ),
541
- sa.PrimaryKeyConstraint("id", name=op.f("pk_watches")),
542
- sa.UniqueConstraint("pipeline_id", "code", name=op.f("uq_watches_pipeline_id_code")),
543
- )
544
- with op.batch_alter_table("watches", schema=None) as batch_op:
545
- batch_op.create_index(batch_op.f("ix_watches_pipeline_id"), ["pipeline_id"], unique=False)
546
- batch_op.create_index(batch_op.f("ix_watches_trigger_document_id"), ["trigger_document_id"], unique=False)
547
-
548
441
  op.create_table(
549
442
  "sources",
550
443
  sa.Column("id", sa.Uuid(), nullable=False),
@@ -556,10 +449,20 @@ def upgrade() -> None:
556
449
  sa.Column("config", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False),
557
450
  sa.Column("token_hash", sa.String(length=128), nullable=True),
558
451
  sa.Column("token_prefix", sa.String(length=16), nullable=True),
452
+ sa.Column("signing_secret", sa.LargeBinary(), nullable=True),
453
+ sa.Column("signing_key_id", sa.String(length=64), nullable=True),
559
454
  sa.Column("paused", sa.Boolean(), server_default=sa.false(), nullable=False),
560
455
  sa.Column("accepted", sa.Integer(), server_default=sa.text("0"), nullable=False),
561
456
  sa.Column("refused", sa.Integer(), server_default=sa.text("0"), nullable=False),
562
457
  sa.Column("last_input_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
458
+ sa.Column("next_poll_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
459
+ sa.Column("polled_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
460
+ sa.Column("cursor", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True),
461
+ sa.Column("cursor_for", sa.String(length=71), nullable=True),
462
+ sa.Column("lease_owner", sa.String(length=200), nullable=True),
463
+ sa.Column("lease_expires_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
464
+ sa.Column("failures", sa.Integer(), server_default=sa.text("0"), nullable=False),
465
+ sa.Column("last_error", sa.Text(), nullable=True),
563
466
  sa.Column(
564
467
  "created_at",
565
468
  dirigent_core.types.UtcDateTime(timezone=True),
@@ -580,6 +483,7 @@ def upgrade() -> None:
580
483
  sa.UniqueConstraint("token_hash", name=op.f("uq_sources_token_hash")),
581
484
  )
582
485
  with op.batch_alter_table("sources", schema=None) as batch_op:
486
+ batch_op.create_index(batch_op.f("ix_sources_next_poll_at"), ["next_poll_at"], unique=False)
583
487
  batch_op.create_index(batch_op.f("ix_sources_pipeline_id"), ["pipeline_id"], unique=False)
584
488
 
585
489
  op.create_table(
@@ -642,12 +546,10 @@ def upgrade() -> None:
642
546
  sa.Enum(
643
547
  "adhoc",
644
548
  "schedule",
645
- "webhook",
646
549
  "api_token",
647
550
  "user",
648
551
  "pipeline",
649
552
  "backfill",
650
- "watch",
651
553
  name="trigger_kind",
652
554
  native_enum=False,
653
555
  length=32,
@@ -868,43 +770,6 @@ def upgrade() -> None:
868
770
  batch_op.create_index("ix_schedule_firings_created_at", ["created_at"], unique=False)
869
771
  batch_op.create_index("ix_schedule_firings_schedule_id_id", ["schedule_id", "id"], unique=False)
870
772
 
871
- op.create_table(
872
- "webhook_deliveries",
873
- sa.Column("id", sa.BigInteger().with_variant(sa.Integer(), "sqlite"), autoincrement=True, nullable=False),
874
- sa.Column("webhook_id", sa.Uuid(), nullable=False),
875
- sa.Column("run_id", sa.Uuid(), nullable=True),
876
- sa.Column(
877
- "created_at",
878
- dirigent_core.types.UtcDateTime(timezone=True),
879
- server_default=sa.func.now(),
880
- nullable=False,
881
- ),
882
- sa.Column(
883
- "outcome",
884
- sa.Enum("accepted", "skipped", "rejected", name="webhook_outcome", native_enum=False, length=32),
885
- nullable=False,
886
- ),
887
- sa.Column("reason", sa.Text(), nullable=True),
888
- sa.Column("payload", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True),
889
- sa.Column(
890
- "mapped_params", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True
891
- ),
892
- sa.Column("source", sa.String(length=64), nullable=True),
893
- sa.ForeignKeyConstraint(
894
- ["run_id"], ["runs.id"], name=op.f("fk_webhook_deliveries_run_id_runs"), ondelete="SET NULL"
895
- ),
896
- sa.ForeignKeyConstraint(
897
- ["webhook_id"],
898
- ["webhook_triggers.id"],
899
- name=op.f("fk_webhook_deliveries_webhook_id_webhook_triggers"),
900
- ondelete="CASCADE",
901
- ),
902
- sa.PrimaryKeyConstraint("id", name=op.f("pk_webhook_deliveries")),
903
- )
904
- with op.batch_alter_table("webhook_deliveries", schema=None) as batch_op:
905
- batch_op.create_index("ix_webhook_deliveries_created_at", ["created_at"], unique=False)
906
- batch_op.create_index("ix_webhook_deliveries_webhook_id_id", ["webhook_id", "id"], unique=False)
907
-
908
773
  op.create_table(
909
774
  "source_inputs",
910
775
  sa.Column("id", sa.BigInteger().with_variant(sa.Integer(), "sqlite"), autoincrement=True, nullable=False),
@@ -912,15 +777,16 @@ def upgrade() -> None:
912
777
  sa.Column("run_id", sa.Uuid(), nullable=True),
913
778
  sa.Column(
914
779
  "outcome",
915
- sa.Enum("accepted", "refused", name="input_outcome", native_enum=False, length=32),
780
+ sa.Enum("accepted", "replied", "refused", "timed_out", name="input_outcome", native_enum=False, length=32),
916
781
  nullable=False,
917
782
  ),
918
- sa.Column("status", sa.Integer(), nullable=False),
783
+ sa.Column("status", sa.Integer(), nullable=True),
919
784
  sa.Column("code", sa.String(length=128), nullable=True),
920
785
  sa.Column("reason", sa.Text(), nullable=True),
921
786
  sa.Column("params", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True),
922
- sa.Column("method", sa.String(length=16), nullable=False),
923
- sa.Column("path", sa.String(length=2000), nullable=False),
787
+ sa.Column("method", sa.String(length=16), nullable=True),
788
+ sa.Column("path", sa.String(length=2000), nullable=True),
789
+ sa.Column("label", sa.String(length=2000), nullable=True),
924
790
  sa.Column("body_bytes", sa.Integer(), server_default=sa.text("0"), nullable=False),
925
791
  sa.Column("duration_ms", sa.Integer(), server_default=sa.text("0"), nullable=False),
926
792
  sa.Column("caller", sa.String(length=64), nullable=True),
@@ -1133,11 +999,6 @@ def downgrade() -> None:
1133
999
  batch_op.drop_index("ix_step_attempts_lease_expires_at")
1134
1000
 
1135
1001
  op.drop_table("step_attempts")
1136
- with op.batch_alter_table("webhook_deliveries", schema=None) as batch_op:
1137
- batch_op.drop_index("ix_webhook_deliveries_webhook_id_id")
1138
- batch_op.drop_index("ix_webhook_deliveries_created_at")
1139
-
1140
- op.drop_table("webhook_deliveries")
1141
1002
  with op.batch_alter_table("source_inputs", schema=None) as batch_op:
1142
1003
  batch_op.drop_index("ix_source_inputs_source_id_id")
1143
1004
  batch_op.drop_index("ix_source_inputs_created_at")
@@ -1167,11 +1028,6 @@ def downgrade() -> None:
1167
1028
  batch_op.drop_index("ix_runs_finished_at")
1168
1029
 
1169
1030
  op.drop_table("runs")
1170
- with op.batch_alter_table("watches", schema=None) as batch_op:
1171
- batch_op.drop_index(batch_op.f("ix_watches_trigger_document_id"))
1172
- batch_op.drop_index(batch_op.f("ix_watches_pipeline_id"))
1173
-
1174
- op.drop_table("watches")
1175
1031
  with op.batch_alter_table("source_routes", schema=None) as batch_op:
1176
1032
  batch_op.drop_index(batch_op.f("ix_source_routes_source_id"))
1177
1033
 
@@ -1180,11 +1036,6 @@ def downgrade() -> None:
1180
1036
  batch_op.drop_index(batch_op.f("ix_sources_pipeline_id"))
1181
1037
 
1182
1038
  op.drop_table("sources")
1183
- with op.batch_alter_table("webhook_triggers", schema=None) as batch_op:
1184
- batch_op.drop_index(batch_op.f("ix_webhook_triggers_trigger_document_id"))
1185
- batch_op.drop_index(batch_op.f("ix_webhook_triggers_pipeline_id"))
1186
-
1187
- op.drop_table("webhook_triggers")
1188
1039
  with op.batch_alter_table("schedules", schema=None) as batch_op:
1189
1040
  batch_op.drop_index(batch_op.f("ix_schedules_trigger_document_id"))
1190
1041
  batch_op.drop_index(batch_op.f("ix_schedules_pipeline_id"))
@@ -24,7 +24,7 @@ builds forms from -- so it cannot drift from the code: a field added to a block
24
24
  up here or fails the build.
25
25
 
26
26
  What a block is, and the contract behind these tables, is in
27
- [the design document](design.md#2-the-six-plugin-surfaces). The short version:
27
+ [the design document](design.md#2-the-seven-plugin-surfaces). The short version:
28
28
 
29
29
  - An **operator** does work. It either finishes and returns its output, or hands back a
30
30
  handle for the engine to probe, which is how a step waits for an hour without holding a
@@ -50,11 +50,11 @@ Two properties are worth reading before a block is used:
50
50
 
51
51
  SURFACES_PREAMBLE: Final = """## Other surfaces
52
52
 
53
- Blocks are two of the six surfaces a plugin contributes to. Three of the other four -- storage
54
- backends, notifiers and connection kinds -- are listed here by id, since a document references
55
- them by code and never by id of any other kind. The sixth, a JSON Schema format checker, is in
56
- [JSON Schema](json-schema.md). Source kinds, which a source node names as ``source.kind``, are
57
- listed after them; see [Source and reply](concepts.md#source-and-reply).
53
+ Blocks are two of the seven surfaces a plugin contributes to. Storage backends, notifiers and
54
+ connection kinds are listed here by id, since a document references them by code and never by
55
+ id of any other kind. Source kinds, which a source node names as ``source.kind``, are listed
56
+ after them; see [Source and reply](concepts.md#source-and-reply). The seventh, a JSON Schema
57
+ format checker, is in [JSON Schema](json-schema.md).
58
58
  """
59
59
 
60
60
  NO_DESCRIPTION: Final = "--"
@@ -287,9 +287,6 @@ class Settings(BaseSettings):
287
287
  retention_logs: Duration | None = None
288
288
  """How long a log entry is kept, for runs too young to be pruned themselves."""
289
289
 
290
- retention_deliveries: Duration | None = None
291
- """How long a webhook delivery is kept."""
292
-
293
290
  retention_inputs: Duration | None = None
294
291
  """How long the record of an input that reached a source is kept."""
295
292
 
@@ -326,12 +323,6 @@ class Settings(BaseSettings):
326
323
  scheduler_lock_key: int = Field(default=0x64_69_72_67)
327
324
  """The advisory-lock key leadership is taken on; only PostgreSQL has one to take."""
328
325
 
329
- watch_backoff: Duration = Field(default=timedelta(seconds=5), gt=timedelta(0))
330
- """How long a watch waits before arming again after its sensor failed; later failures double it."""
331
-
332
- watch_backoff_max: Duration = Field(default=timedelta(minutes=5), gt=timedelta(0))
333
- """The longest a watch backs off, however many waits in a row have failed."""
334
-
335
326
  apply_dir: Path | None = None
336
327
  """A directory of pipeline documents the server applies at boot; unset applies nothing."""
337
328
 
@@ -350,14 +341,6 @@ class Settings(BaseSettings):
350
341
  notification_lease: Duration = Field(default=timedelta(seconds=60), ge=timedelta(seconds=5))
351
342
  """How long a claimed notification's lease is valid before another worker may take it."""
352
343
 
353
- webhook_max_payload: Size = Field(default=1_048_576, ge=1)
354
- """The largest body the webhook intake will read; reading stops one byte past it."""
355
-
356
- webhook_intake_rate_per_minute: int = Field(default=120, ge=1)
357
- """How fast one *offered* token may deliver, before anything is looked up.
358
-
359
- A real webhook's own ``rate_limit_per_minute`` applies on top of this."""
360
-
361
344
  endpoint_prefix: str = "/endpoints"
362
345
  """Where endpoint sources are served, outside the versioned API.
363
346
 
@@ -380,8 +363,34 @@ class Settings(BaseSettings):
380
363
 
381
364
  A call past it is answered 503 before its run is created."""
382
365
 
366
+ source_max_waiting_per_source: int = Field(default=64, ge=1)
367
+ """How many of those callers one source may hold, so one slow pipeline cannot take every seat.
368
+
369
+ A call past it is answered 503 before its run is created."""
370
+
383
371
  source_reply_poll: Duration = Field(default=timedelta(milliseconds=50), gt=timedelta(0))
384
- """How often one server process reads the replies its held callers wait for, in one query."""
372
+ """The least time between two reads of the replies one server process's held callers wait for."""
373
+
374
+ source_poll_tick: Duration = Field(default=timedelta(seconds=5), gt=timedelta(0))
375
+ """How often a worker looks for polled sources that are due."""
376
+
377
+ source_poll_lease: Duration = Field(default=timedelta(minutes=5), gt=timedelta(0))
378
+ """How long a worker holds a worker-hosted source without renewing; another may take it once this passes."""
379
+
380
+ source_poll_batch: int = Field(default=100, ge=1)
381
+ """The most inputs one poll may hand over, before the source's own ``max_in_flight``."""
382
+
383
+ source_held_max: int = Field(default=16, ge=1)
384
+ """How many held sources, such as queue consumers, one worker keeps open at once."""
385
+
386
+ source_hold_wait: Duration = Field(default=timedelta(seconds=1), gt=timedelta(0))
387
+ """How long a held source's consumer waits for a message before the worker checks on it."""
388
+
389
+ source_backoff: Duration = Field(default=timedelta(seconds=30), gt=timedelta(0))
390
+ """How long a polled source waits after a failed poll; later failures double it."""
391
+
392
+ source_backoff_max: Duration = Field(default=timedelta(minutes=30), gt=timedelta(0))
393
+ """The longest a polled source backs off, however many polls in a row have failed."""
385
394
 
386
395
  login_rate_per_minute: int = Field(default=10, ge=1)
387
396
  """How many login attempts one address, and one username, get a minute.
@@ -109,8 +109,8 @@ async def prune_absent(
109
109
  The narrowing is by the CURRENT version's provenance: what a directory applied last, a
110
110
  directory may take away. Deactivation pauses nothing retroactively beyond what
111
111
  deactivation always means, and history survives untouched. A triggers document has no
112
- history worth keeping, so it is deleted instead, and the cascade takes the schedules and
113
- webhooks it owned -- never the pipeline's own inline ones. A schema is never pruned.
112
+ history worth keeping, so it is deleted instead, and the cascade takes the schedules
113
+ it owned -- never the pipeline's own inline ones. A schema is never pruned.
114
114
  """
115
115
  rows = await session.execute(
116
116
  sa.select(Pipeline.code)
@@ -132,7 +132,7 @@ async def prune_absent(
132
132
  documents = await delete_absent_trigger_documents(session, services, keep, dry_run=dry_run)
133
133
  if not dry_run:
134
134
  for code in absent:
135
- await set_active(session, services, code, active=False)
135
+ await set_active(session, code, active=False)
136
136
  return PruneResult(pruned=absent, trigger_documents_removed=documents, dry_run=dry_run)
137
137
 
138
138
 
@@ -13,14 +13,13 @@ from jsonschema.exceptions import SchemaError
13
13
  from jsonschema.validators import extend as extend_validator # pyright: ignore[reportUnknownVariableType]
14
14
  from pydantic import BaseModel, ConfigDict, Field, JsonValue, ValidationError
15
15
 
16
- from dirigent_client.schemas import BlockKind, Catalog, ValidationIssue
16
+ from dirigent_client.schemas import Catalog, ValidationIssue
17
17
  from dirigent_common import STORAGE_URI_FORMAT, Issue, JsonMap, Message, base_format_checker, validation_issue
18
18
  from dirigent_core.engine.definition import (
19
19
  FORMAT_V1,
20
20
  KIND_PIPELINE,
21
21
  KIND_TRIGGERS,
22
22
  Document,
23
- ParameterError,
24
23
  PipelineDefinition,
25
24
  TriggerRule,
26
25
  TriggersDefinition,
@@ -75,8 +74,6 @@ from dirigent_core.messages import (
75
74
  TARGET_PIPELINE_INACTIVE,
76
75
  TARGET_PIPELINE_MISSING,
77
76
  UNKNOWN_DOCUMENT_KIND,
78
- WATCH_PARAMS_REFUSED,
79
- WEBHOOK_MAPPING_REFUSED,
80
77
  WORKER_TAGS_MISSING,
81
78
  WRONG_DOCUMENT_KIND,
82
79
  WRONG_FORMAT,
@@ -392,10 +389,9 @@ def validate_against_catalog(
392
389
 
393
390
  kinds = source_kinds if source_kinds is not None else built_in_kinds()
394
391
  checker = format_checker if format_checker is not None else base_format_checker()
395
- sensors = _sensors(catalog) if check_blocks else None
396
392
  if isinstance(definition, TriggersDefinition):
397
393
  return _triggers_document_issues(
398
- definition, checker, target, check_target=check_blocks and pipelines is not None, sensors=sensors
394
+ definition, checker, target, check_target=check_blocks and pipelines is not None
399
395
  )
400
396
  # A document that brings its own connections or schemas satisfies its own references to them.
401
397
  known = None if connections is None else {name for name in connections} | set(definition.connections)
@@ -417,7 +413,7 @@ def validate_against_catalog(
417
413
  issues.extend(malformed_params)
418
414
  issues.extend(_carried_schema_issues(definition))
419
415
  issues.extend(_reference_issues(definition))
420
- issues.extend(_trigger_issues(definition, checker, check_params=not malformed_params, sensors=sensors))
416
+ issues.extend(_trigger_issues(definition, checker, check_params=not malformed_params))
421
417
  issues.extend(source_issues(definition, kinds))
422
418
  if check_blocks:
423
419
  issues.extend(
@@ -492,27 +488,19 @@ def _carried_schema_issues(definition: PipelineDefinition) -> list[ValidationIss
492
488
  return issues
493
489
 
494
490
 
495
- def _sensors(catalog: Catalog) -> frozenset[str]:
496
- """Name the installed blocks that are sensors, which are the only ones a watch can wait on."""
497
- return frozenset(entry.id for entry in catalog.blocks if entry.kind is BlockKind.SENSOR)
498
-
499
-
500
491
  def _trigger_issues(
501
492
  definition: Document,
502
493
  format_checker: FormatChecker,
503
494
  *,
504
495
  check_params: bool = True,
505
496
  against: PipelineDefinition | None = None,
506
- sensors: frozenset[str] | None = None,
507
497
  ) -> list[ValidationIssue]:
508
498
  """Check the clocks a document declares, the parameters its triggers pin, and its mappings.
509
499
 
510
500
  ``check_params`` is off when the parameter schema is not itself a schema, which is already
511
501
  reported and would otherwise be reported again once per trigger. ``against`` is the
512
502
  definition whose parameter schema the pins and the mappings are checked against, which for
513
- a triggers document is another document's current version, and whose steps a watch is
514
- checked against. ``sensors`` names the installed sensors; unset, a watched step's block
515
- is not checked.
503
+ a triggers document is another document's current version.
516
504
  """
517
505
  from dirigent_core.triggers.schedules import (
518
506
  ScheduleError,
@@ -520,7 +508,6 @@ def _trigger_issues(
520
508
  check_schedule,
521
509
  check_schedule_params,
522
510
  )
523
- from dirigent_core.triggers.webhooks import WebhookError, check_webhook_mapping
524
511
 
525
512
  pins_against = against if against is not None else definition
526
513
  issues: list[ValidationIssue] = []
@@ -547,61 +534,6 @@ def _trigger_issues(
547
534
  detail=str(error).strip(),
548
535
  )
549
536
  )
550
- if isinstance(pins_against, PipelineDefinition):
551
- issues.extend(
552
- _watch_issues(definition, pins_against, format_checker, check_params=check_params, sensors=sensors)
553
- )
554
- if not check_params or not isinstance(pins_against, PipelineDefinition):
555
- return issues
556
- for index, hook in enumerate(definition.triggers.webhooks):
557
- try:
558
- check_webhook_mapping(pins_against, hook.params_from_payload)
559
- except WebhookError as error:
560
- issues.append(
561
- ValidationIssue.of(
562
- WEBHOOK_MAPPING_REFUSED,
563
- location=f"triggers.webhooks[{index}].params_from_payload",
564
- detail=str(error).strip(),
565
- )
566
- )
567
- return issues
568
-
569
-
570
- def _watch_issues(
571
- definition: Document,
572
- against: PipelineDefinition,
573
- format_checker: FormatChecker,
574
- *,
575
- check_params: bool,
576
- sensors: frozenset[str] | None,
577
- ) -> list[ValidationIssue]:
578
- """Check that each watch waits on a root sensor step of the pipeline, with pins it accepts."""
579
- from dirigent_core.triggers.watches import watch_issue
580
-
581
- issues: list[ValidationIssue] = []
582
- for index, watch in enumerate(definition.triggers.watches):
583
- refusal = watch_issue(watch.code, watch.step, against, sensors)
584
- if refusal is not None:
585
- issues.append(
586
- ValidationIssue(
587
- location=f"triggers.watches[{index}].step",
588
- message=refusal.message,
589
- code=refusal.code,
590
- params=refusal.params,
591
- )
592
- )
593
- if not check_params:
594
- continue
595
- try:
596
- against.validate_params(dict(watch.params), format_checker)
597
- except ParameterError as error:
598
- issues.append(
599
- ValidationIssue.of(
600
- WATCH_PARAMS_REFUSED,
601
- location=f"triggers.watches[{index}].params",
602
- detail=str(error).strip(),
603
- )
604
- )
605
537
  return issues
606
538
 
607
539
 
@@ -611,7 +543,6 @@ def _triggers_document_issues(
611
543
  target: TriggerTarget | None,
612
544
  *,
613
545
  check_target: bool,
614
- sensors: frozenset[str] | None = None,
615
546
  ) -> list[ValidationIssue]:
616
547
  """Check a triggers document: the pipeline it names, its clocks, and the pins they carry."""
617
548
  if not check_target:
@@ -624,7 +555,7 @@ def _triggers_document_issues(
624
555
  from dirigent_core.sources.rules import triggers_issues
625
556
 
626
557
  return triggers_issues(target.code, definition.triggers)
627
- return _trigger_issues(definition, format_checker, against=target.definition, sensors=sensors)
558
+ return _trigger_issues(definition, format_checker, against=target.definition)
628
559
 
629
560
 
630
561
  def _requirement_issues(
@@ -1058,8 +989,6 @@ class DocumentSummary(BaseModel):
1058
989
  steps: list[str] = Field(default_factory=list[str])
1059
990
  blocks: list[str] = Field(default_factory=list[str])
1060
991
  schedules: list[str] = Field(default_factory=list[str])
1061
- webhooks: list[str] = Field(default_factory=list[str])
1062
- watches: list[str] = Field(default_factory=list[str])
1063
992
  digest: str
1064
993
 
1065
994
 
@@ -1081,7 +1010,5 @@ def summarize(definition: Document) -> DocumentSummary:
1081
1010
  steps=steps,
1082
1011
  blocks=blocks,
1083
1012
  schedules=[spec.code for spec in definition.triggers.schedules],
1084
- webhooks=[spec.code for spec in definition.triggers.webhooks],
1085
- watches=[spec.code for spec in definition.triggers.watches],
1086
1013
  digest=digest_of(definition),
1087
1014
  )
@@ -24,7 +24,6 @@ from dirigent_core.engine.definition import (
24
24
  TriggerRule,
25
25
  TriggersDefinition,
26
26
  TriggerSpecs,
27
- WebhookSpec,
28
27
  canonical_document,
29
28
  dump_definition,
30
29
  load_definition,
@@ -93,7 +92,6 @@ __all__ = [
93
92
  "TriggerSpecs",
94
93
  "TriggersDefinition",
95
94
  "UnknownReference",
96
- "WebhookSpec",
97
95
  "advance",
98
96
  "aggregate_items",
99
97
  "backoff_delay",