agenticdome-python-sdk 1.2.38__tar.gz → 1.2.39__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 (98) hide show
  1. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/PKG-INFO +7 -3
  2. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/README.md +6 -2
  3. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_python_sdk.egg-info/PKG-INFO +7 -3
  4. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/catalog_snapshot.json +3 -3
  5. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/onboarding_cli.py +135 -5
  6. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/customer-onboarding.md +11 -2
  7. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/pyproject.toml +1 -1
  8. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/CONTRIBUTING.md +0 -0
  9. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/LICENSE +0 -0
  10. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/MANIFEST.in +0 -0
  11. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/NOTICE +0 -0
  12. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/SECURITY.md +0 -0
  13. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_python_sdk.egg-info/SOURCES.txt +0 -0
  14. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_python_sdk.egg-info/dependency_links.txt +0 -0
  15. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_python_sdk.egg-info/entry_points.txt +0 -0
  16. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_python_sdk.egg-info/requires.txt +0 -0
  17. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_python_sdk.egg-info/top_level.txt +0 -0
  18. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/__init__.py +0 -0
  19. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/_framework_firewall.py +0 -0
  20. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/_mode.py +0 -0
  21. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/action_path_proof.py +0 -0
  22. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/agno.py +0 -0
  23. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/attestation.py +0 -0
  24. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/autogen.py +0 -0
  25. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/aws_bedrock.py +0 -0
  26. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/claude.py +0 -0
  27. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/client.py +0 -0
  28. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/connect.py +0 -0
  29. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/copilot_ir.py +0 -0
  30. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/copilot_ir_collector.cjs +0 -0
  31. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/crewai.py +0 -0
  32. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/demo.py +0 -0
  33. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/existing_integration.py +0 -0
  34. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/framework_edits.py +0 -0
  35. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/generic_python.py +0 -0
  36. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/google_adk.py +0 -0
  37. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/guided_integration.py +0 -0
  38. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/hook_catalog.py +0 -0
  39. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/identity.py +0 -0
  40. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/langgraph.py +0 -0
  41. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/lifecycle.py +0 -0
  42. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/llamaindex.py +0 -0
  43. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/mcp_host.py +0 -0
  44. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/mcp_http_gateway.py +0 -0
  45. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/mcp_rehearsal_server.py +0 -0
  46. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/mcp_verification.py +0 -0
  47. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/microsoft_agent_framework.py +0 -0
  48. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/microsoft_ai_foundry.py +0 -0
  49. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/openai_agents.py +0 -0
  50. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/outcomes.py +0 -0
  51. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/proof.py +0 -0
  52. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/pydantic.py +0 -0
  53. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/simulation.py +0 -0
  54. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/agenticdome_sdk/smolagents.py +0 -0
  55. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/README.md +0 -0
  56. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/agno.md +0 -0
  57. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/autogen.md +0 -0
  58. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/aws-bedrock.md +0 -0
  59. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/claude-agent-sdk.md +0 -0
  60. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/crewai.md +0 -0
  61. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/custom-python.md +0 -0
  62. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/google-adk.md +0 -0
  63. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/langgraph.md +0 -0
  64. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/llamaindex.md +0 -0
  65. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/microsoft-agent-framework.md +0 -0
  66. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/microsoft-ai-foundry.md +0 -0
  67. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/openai-agents.md +0 -0
  68. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/pydanticai.md +0 -0
  69. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/frameworks/smolagents.md +0 -0
  70. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/mcp-integration.md +0 -0
  71. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/performance-evidence.md +0 -0
  72. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/docs/runtime-deployment.md +0 -0
  73. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/PRODUCTION_INTEGRATION.md +0 -0
  74. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/README.md +0 -0
  75. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/attack_demo.py +0 -0
  76. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/crewai_agenticdome_firewall.py +0 -0
  77. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/README.md +0 -0
  78. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/_shared.py +0 -0
  79. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/agno.py +0 -0
  80. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/autogen.py +0 -0
  81. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/aws_bedrock.py +0 -0
  82. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/claude_agent_sdk.py +0 -0
  83. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/crewai.py +0 -0
  84. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/custom_python.py +0 -0
  85. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/google_adk.py +0 -0
  86. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/langgraph.py +0 -0
  87. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/llamaindex.py +0 -0
  88. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/mcp.py +0 -0
  89. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/microsoft_agent_framework.py +0 -0
  90. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/microsoft_ai_foundry.py +0 -0
  91. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/openai_agents.py +0 -0
  92. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/pydanticai.py +0 -0
  93. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/frameworks/smolagents.py +0 -0
  94. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/langgraph_agenticdome_firewall.py +0 -0
  95. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/local_simulation_gallery.py +0 -0
  96. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/examples/mcp_gateway_action_firewall.py +0 -0
  97. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/setup.cfg +0 -0
  98. {agenticdome_python_sdk-1.2.38 → agenticdome_python_sdk-1.2.39}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agenticdome-python-sdk
3
- Version: 1.2.38
3
+ Version: 1.2.39
4
4
  Summary: Official Python SDK for AgenticDome security guardrails, DLP, tool authorization, and multi-agent delegation enforcement.
5
5
  Author: AgenticDome
6
6
  License-Expression: Apache-2.0
@@ -186,8 +186,12 @@ attach other boundaries manually, then run
186
186
  If a coding assistant has already made and tested the application edits,
187
187
  import its canonical `AgenticDome_Integration.md` in the customer portal and
188
188
  run `agenticdome validate-report --report AgenticDome_Integration.md` from
189
- that same deployable workload. Import the two generated, hash-bound JSON files
190
- in Onboarding. This avoids repeating the guided edit plan; the Markdown counts
189
+ that same deployable workload. If it reports `ready=false`, follow the local
190
+ `.agenticdome/RECONCILIATION.md` worklist and rerun after reviewing the real
191
+ source and tests. You may import the two generated, hash-bound JSON files in
192
+ Onboarding even when the gate is incomplete, to retain partial evidence and run
193
+ a fixed tenant-policy diagnostic. They do not count as passing workload proof or
194
+ unlock activation; rerun and reimport after closing the gaps. This avoids repeating the guided edit plan; the Markdown counts
191
195
  remain AI-reported and a real action-path test is still required before
192
196
  claiming protection. See the [customer onboarding guide](docs/customer-onboarding.md).
193
197
 
@@ -73,8 +73,12 @@ attach other boundaries manually, then run
73
73
  If a coding assistant has already made and tested the application edits,
74
74
  import its canonical `AgenticDome_Integration.md` in the customer portal and
75
75
  run `agenticdome validate-report --report AgenticDome_Integration.md` from
76
- that same deployable workload. Import the two generated, hash-bound JSON files
77
- in Onboarding. This avoids repeating the guided edit plan; the Markdown counts
76
+ that same deployable workload. If it reports `ready=false`, follow the local
77
+ `.agenticdome/RECONCILIATION.md` worklist and rerun after reviewing the real
78
+ source and tests. You may import the two generated, hash-bound JSON files in
79
+ Onboarding even when the gate is incomplete, to retain partial evidence and run
80
+ a fixed tenant-policy diagnostic. They do not count as passing workload proof or
81
+ unlock activation; rerun and reimport after closing the gaps. This avoids repeating the guided edit plan; the Markdown counts
78
82
  remain AI-reported and a real action-path test is still required before
79
83
  claiming protection. See the [customer onboarding guide](docs/customer-onboarding.md).
80
84
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agenticdome-python-sdk
3
- Version: 1.2.38
3
+ Version: 1.2.39
4
4
  Summary: Official Python SDK for AgenticDome security guardrails, DLP, tool authorization, and multi-agent delegation enforcement.
5
5
  Author: AgenticDome
6
6
  License-Expression: Apache-2.0
@@ -186,8 +186,12 @@ attach other boundaries manually, then run
186
186
  If a coding assistant has already made and tested the application edits,
187
187
  import its canonical `AgenticDome_Integration.md` in the customer portal and
188
188
  run `agenticdome validate-report --report AgenticDome_Integration.md` from
189
- that same deployable workload. Import the two generated, hash-bound JSON files
190
- in Onboarding. This avoids repeating the guided edit plan; the Markdown counts
189
+ that same deployable workload. If it reports `ready=false`, follow the local
190
+ `.agenticdome/RECONCILIATION.md` worklist and rerun after reviewing the real
191
+ source and tests. You may import the two generated, hash-bound JSON files in
192
+ Onboarding even when the gate is incomplete, to retain partial evidence and run
193
+ a fixed tenant-policy diagnostic. They do not count as passing workload proof or
194
+ unlock activation; rerun and reimport after closing the gaps. This avoids repeating the guided edit plan; the Markdown counts
191
195
  remain AI-reported and a real action-path test is still required before
192
196
  claiming protection. See the [customer onboarding guide](docs/customer-onboarding.md).
193
197
 
@@ -2,12 +2,12 @@
2
2
  "schema": "agenticdome.catalog-snapshot.v1",
3
3
  "catalog_schema": "agenticdome.hook-catalog.v1",
4
4
  "verified_at": "2026-10-02",
5
- "python_sdk_version": "1.2.38",
5
+ "python_sdk_version": "1.2.39",
6
6
  "published_packages": {
7
7
  "agenticdome-python-sdk": {
8
8
  "registry": "pypi",
9
- "version": "1.2.38",
10
- "url": "https://pypi.org/project/agenticdome-python-sdk/1.2.38/"
9
+ "version": "1.2.39",
10
+ "url": "https://pypi.org/project/agenticdome-python-sdk/1.2.39/"
11
11
  },
12
12
  "agenticdome-sdk": {
13
13
  "registry": "npm",
@@ -43,6 +43,10 @@ from .framework_edits import has_explicit_python_tool_dispatcher
43
43
  SCHEMA = "agenticdome.onboarding-report.v1"
44
44
  CONFIG_SCHEMA = "agenticdome.project-config.v1"
45
45
  COPILOT_ANALYSIS_REVISION = 4
46
+ # Local cache revision is separate from the wire contract: changing the
47
+ # analyzer's finding rules must refresh old on-disk results without forcing
48
+ # every already-published SDK to upgrade in lockstep with its sidecar.
49
+ COPILOT_CACHE_REVISION = 2
46
50
  COPILOT_MAX_WORKLOAD_BYTES = 4_000_000
47
51
  COPILOT_MAX_WORKLOAD_PARTS = 24
48
52
  COPILOT_MAX_EVENTS_PER_FUNCTION = 2_048
@@ -575,6 +579,7 @@ def _copilot_semantic_analysis(root: Path, ir: Dict[str, Any], *, required: bool
575
579
  and isinstance(semantic, dict)
576
580
  and semantic.get("ir_sha256") == ir_digest
577
581
  and semantic.get("analysis_revision") == COPILOT_ANALYSIS_REVISION
582
+ and cached.get("cache_revision") == COPILOT_CACHE_REVISION
578
583
  ):
579
584
  return semantic
580
585
 
@@ -592,12 +597,14 @@ def _copilot_semantic_analysis(root: Path, ir: Dict[str, Any], *, required: bool
592
597
  or part_cache.get("api_base") != api_base
593
598
  or part_cache.get("catalog_digest") != expected_catalog_digest
594
599
  or part_cache.get("selected_ir_sha256") != ir_digest
600
+ or part_cache.get("cache_revision") != COPILOT_CACHE_REVISION
595
601
  or not isinstance(part_cache.get("parts"), dict)
596
602
  ):
597
603
  part_cache = {
598
604
  "schema": "agenticdome.copilot-part-cache.v1", "tenant_id": tenant_id,
599
605
  "api_base": api_base, "catalog_digest": expected_catalog_digest,
600
- "selected_ir_sha256": ir_digest, "parts": {},
606
+ "selected_ir_sha256": ir_digest, "cache_revision": COPILOT_CACHE_REVISION,
607
+ "parts": {},
601
608
  }
602
609
  analyzed: List[Tuple[str, Dict[str, Any]]] = []
603
610
  response_binding: Dict[str, Any] = {}
@@ -628,7 +635,7 @@ def _copilot_semantic_analysis(root: Path, ir: Dict[str, Any], *, required: bool
628
635
  if len(body) > 4_500_000:
629
636
  raise SystemExit(f"Bounded Copilot unit {name} exceeds its transport budget; no partial plan was saved.")
630
637
  idempotency_key = hashlib.sha256(
631
- f"{tenant_id}\n{api_base}\n{part_digest}\n{expected_catalog_digest}".encode("utf-8")
638
+ f"{tenant_id}\n{api_base}\n{part_digest}\n{expected_catalog_digest}\n{COPILOT_CACHE_REVISION}".encode("utf-8")
632
639
  ).hexdigest()
633
640
  try:
634
641
  result = _post_copilot(api_base, api_key, tenant_id, "/integration-copilot/v1/analyze", body, idempotency_key=idempotency_key)
@@ -664,6 +671,7 @@ def _copilot_semantic_analysis(root: Path, ir: Dict[str, Any], *, required: bool
664
671
  "source_upload": False,
665
672
  "tenant_id": tenant_id,
666
673
  "api_base": api_base,
674
+ "cache_revision": COPILOT_CACHE_REVISION,
667
675
  "semantic_analysis": semantic,
668
676
  "catalog_binding": response_binding,
669
677
  })
@@ -1154,6 +1162,23 @@ def _write_json(path: Path, value: Dict[str, Any]) -> None:
1154
1162
  os.unlink(staged)
1155
1163
 
1156
1164
 
1165
+ def _write_local_text(path: Path, value: str) -> None:
1166
+ """Replace a local review artifact atomically without following its symlink."""
1167
+ path.parent.mkdir(parents=True, exist_ok=True)
1168
+ if path.is_symlink():
1169
+ raise SystemExit("A reconciliation output path is a symlink; remove it and retry.")
1170
+ descriptor, staged = tempfile.mkstemp(prefix=".agenticdome-review-", dir=path.parent)
1171
+ try:
1172
+ with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
1173
+ handle.write(value)
1174
+ handle.flush()
1175
+ os.fsync(handle.fileno())
1176
+ os.replace(staged, path)
1177
+ finally:
1178
+ if os.path.exists(staged):
1179
+ os.unlink(staged)
1180
+
1181
+
1157
1182
  def _load_json(path: Path) -> Dict[str, Any]:
1158
1183
  try:
1159
1184
  value = json.loads(path.read_text(encoding="utf-8"))
@@ -2562,6 +2587,84 @@ def verify_project(root: Path, live: bool = False, run_tests: bool = False,
2562
2587
  return (0 if result["ready"] else 2), result
2563
2588
 
2564
2589
 
2590
+ def _reconciliation_markdown(plan: Dict[str, Any], verification: Dict[str, Any], report_digest: str) -> str:
2591
+ """Give a bounded, source-free worklist without accepting AI claims as proof."""
2592
+ semantic = plan.get("semantic_analysis") if isinstance(plan.get("semantic_analysis"), dict) else {}
2593
+ gate = verification.get("semantic_gate") if isinstance(verification.get("semantic_gate"), dict) else {}
2594
+
2595
+ def groups(field: str) -> List[Tuple[str, str, int, List[int]]]:
2596
+ grouped: Dict[Tuple[str, str], List[int]] = {}
2597
+ rows = semantic.get(field) if isinstance(semantic.get(field), list) else []
2598
+ for item in rows[:100]:
2599
+ if not isinstance(item, dict):
2600
+ continue
2601
+ boundary = str(item.get("boundary") or "unknown")
2602
+ if boundary not in {"prompt_ingress", "tool_execution", "output_egress"}:
2603
+ boundary = "unknown"
2604
+ raw_path = str(item.get("path") or "")[:500].replace("\\", "/")
2605
+ # These are display paths only. Do not let a remote result inject
2606
+ # Markdown, an absolute path, source text, or a shell instruction.
2607
+ if not raw_path or raw_path.startswith("/") or ".." in Path(raw_path).parts:
2608
+ path = "[invalid relative path]"
2609
+ else:
2610
+ path = re.sub(r"[^A-Za-z0-9_./ -]", "_", raw_path)
2611
+ try:
2612
+ line = max(1, min(int(item.get("line") or 1), 10_000_000))
2613
+ except (TypeError, ValueError):
2614
+ line = 1
2615
+ grouped.setdefault((boundary, path), []).append(line)
2616
+ return [(boundary, path, len(lines), sorted(set(lines))[:12])
2617
+ for (boundary, path), lines in sorted(grouped.items())]
2618
+
2619
+ guidance = {
2620
+ "prompt_ingress": "Review the final assembled model messages at each call. Screen untrusted user, retrieved and tool content immediately before the model; test a blocked input with zero model calls.",
2621
+ "tool_execution": "Trace the actual executor. Authorize the exact final tool name and arguments immediately before it; test BLOCKED = zero handler calls and ALLOWED = one.",
2622
+ "output_egress": "Trace the return to the external response, stream or persistence boundary. Review or redact there, and test that the released value—not merely an internal value—is safe.",
2623
+ "unknown": "Trace this path and identify its real boundary before claiming protection.",
2624
+ }
2625
+ lines = [
2626
+ "# AgenticDome guided reconciliation", "",
2627
+ "This is a local, source-free worklist for the current validation run. It is not a bypass waiver, a code patch, or live tenant proof.",
2628
+ "The AI-authored AgenticDome_Integration.md remains an assertion until these checks and real-path tests pass.", "",
2629
+ f"- AI report SHA-256: `{report_digest}`",
2630
+ f"- Action-required candidates: {int(gate.get('unresolved_bypasses', 0))}",
2631
+ f"- Review-required internal or indirect paths: {int(gate.get('review_required', 0))}",
2632
+ f"- Semantic confidence: {gate.get('confidence', 'unavailable')}", "",
2633
+ ]
2634
+ for field, title in (("bypass_risks", "Action required"), ("review_findings", "Trace and classify")):
2635
+ lines.extend([f"## {title}", ""])
2636
+ found = groups(field)
2637
+ if not found:
2638
+ lines.append("No findings in this category for this run.")
2639
+ for boundary, path, count, positions in found:
2640
+ references = ", ".join(str(number) for number in positions)
2641
+ if count > len(positions):
2642
+ references += ", …"
2643
+ lines.extend([
2644
+ f"### {boundary} · `{path}` ({count} location{'s' if count != 1 else ''})",
2645
+ f"Lines: {references}. {guidance[boundary]}", "",
2646
+ ])
2647
+ lines.append("")
2648
+ tests = verification.get("application_tests") if isinstance(verification.get("application_tests"), dict) else {}
2649
+ report_scope = verification.get("ai_report_scope") if isinstance(verification.get("ai_report_scope"), dict) else {}
2650
+ lines.extend(["## Finish the reconciliation", ""])
2651
+ if report_scope.get("scope_incomplete") or int(report_scope.get("reported_unprotected", 0)) > 0:
2652
+ lines.append("- The AI report itself says a relevant path was NOT ASSESSED or unprotected. Inspect and protect that path, then update the report; its zero-count rows cannot clear this scope gap.")
2653
+ if plan.get("coverage", {}).get("gaps"):
2654
+ lines.append("- Required boundary categories are missing from the inspected workload. Attach and test them before retrying.")
2655
+ if gate.get("cross_part_review_required"):
2656
+ lines.append("- Analysis was split across parts; trace cross-part calls and alternate entry paths. Separate parts are not a proof of guard dominance.")
2657
+ if tests.get("passed") is not True:
2658
+ lines.append("- Workload tests did not pass or were not detected. Run the local test command directly to see its output; no test log is uploaded.")
2659
+ lines.extend([
2660
+ "- Give these grouped paths to your coding assistant together with the real source. Keep the assistant's changes reviewable and update only the canonical AgenticDome_Integration.md.",
2661
+ "- Rerun `agenticdome validate-report --report AgenticDome_Integration.md` after code and test changes. Do not edit the JSON evidence by hand.",
2662
+ "- Even after local `ready=true`, verify a real action through the assigned tenant sidecar and confirm BLOCKED executes zero times and ALLOWED once before going live.",
2663
+ "",
2664
+ ])
2665
+ return "\n".join(lines)
2666
+
2667
+
2565
2668
  def validate_ai_integration_report(root: Path, report_path: Path) -> Tuple[int, Dict[str, Any]]:
2566
2669
  """Validate an AI-assisted edit with the existing semantic and test gates.
2567
2670
 
@@ -2588,6 +2691,7 @@ def validate_ai_integration_report(root: Path, report_path: Path) -> Tuple[int,
2588
2691
  rows = re.findall(r"^\|\s*([1-8])\.([^\r\n]*)", markdown, re.MULTILINE)
2589
2692
  if len(rows) != 8 or {number for number, _ in rows} != {str(index) for index in range(1, 9)}:
2590
2693
  raise SystemExit("The integration report needs one valid row for each of the eight interception categories.")
2694
+ reported_unprotected = 0
2591
2695
  for _, cells_text in rows:
2592
2696
  cells = [cell.strip().replace("*", "").replace(",", "") for cell in cells_text.strip().strip("|").split("|")]
2593
2697
  if len(cells) != 6 or any(not re.fullmatch(r"\d{1,5}", cell) for cell in cells[1:]):
@@ -2595,6 +2699,8 @@ def validate_ai_integration_report(root: Path, report_path: Path) -> Tuple[int,
2595
2699
  found, existing, new, unprotected, live = (int(cell) for cell in cells[1:])
2596
2700
  if any(value > 10000 for value in (found, existing, new, unprotected, live)) or existing + new + unprotected != found or live > existing + new:
2597
2701
  raise SystemExit("The report counts do not reconcile. Correct the AI integration inventory before validation.")
2702
+ reported_unprotected += unprotected
2703
+ scope_incomplete = bool(re.search(r"\bNOT ASSESSED\b", markdown, re.IGNORECASE))
2598
2704
  digest = hashlib.sha256(markdown.encode("utf-8")).hexdigest()
2599
2705
 
2600
2706
  inspection = _exportable_inspection(inspect_repository(root))
@@ -2610,6 +2716,14 @@ def validate_ai_integration_report(root: Path, report_path: Path) -> Tuple[int,
2610
2716
  plan = integration_plan(root)
2611
2717
  plan["inspection_report_sha256"] = inspection["report_sha256"]
2612
2718
  exit_code, verification = verify_project(root, run_tests=True, plan=plan)
2719
+ local_checks_passed = exit_code == 0 and verification.get("ready") is True
2720
+ verification["ai_report_scope"] = {
2721
+ "scope_incomplete": scope_incomplete,
2722
+ "reported_unprotected": reported_unprotected,
2723
+ }
2724
+ if scope_incomplete or reported_unprotected:
2725
+ verification["ready"] = False
2726
+ exit_code = 2
2613
2727
  verification["ai_integration_report_sha256"] = digest
2614
2728
  verification.pop("report_sha256", None)
2615
2729
  verification["report_sha256"] = hashlib.sha256(
@@ -2618,18 +2732,34 @@ def validate_ai_integration_report(root: Path, report_path: Path) -> Tuple[int,
2618
2732
 
2619
2733
  inspection_path = root / "agenticdome-inspection.json"
2620
2734
  verification_path = root / ".agenticdome" / "verification.json"
2621
- for output in (inspection_path, verification_path):
2735
+ reconciliation_path = root / ".agenticdome" / "RECONCILIATION.md"
2736
+ for output in (inspection_path, verification_path, reconciliation_path):
2622
2737
  if output.is_symlink() or not output.resolve().is_relative_to(root):
2623
2738
  raise SystemExit("An evidence output path is a symlink or leaves this workload; remove that path and retry.")
2624
2739
  _write_json(inspection_path, inspection)
2625
2740
  _write_json(verification_path, verification)
2741
+ _write_local_text(reconciliation_path, _reconciliation_markdown(plan, verification, digest))
2742
+ partial_coverage = local_checks_passed and not verification["ready"]
2626
2743
  return exit_code, {
2627
- "status": "validation_passed" if verification["ready"] else "validation_needs_attention",
2744
+ "status": "validation_passed" if verification["ready"] else (
2745
+ "partial_coverage" if partial_coverage else "validation_needs_attention"
2746
+ ),
2628
2747
  "inspection": str(inspection_path.relative_to(root)),
2629
2748
  "verification": str(verification_path.relative_to(root)),
2749
+ "reconciliation": str(reconciliation_path.relative_to(root)),
2630
2750
  "ai_integration_report_sha256": digest,
2631
2751
  "ready": verification["ready"],
2632
- "next_action": "Import both JSON files in Onboarding; then run the assigned-runtime check and test a real action path. No customer action executes in these fixed decision cases.",
2752
+ "next_action": (
2753
+ "Import both JSON files in Onboarding; then run the assigned-runtime check and test a real action path. No customer action executes in these fixed decision cases."
2754
+ if verification["ready"] else (
2755
+ "Import both JSON files as partial evidence and run a tenant-policy diagnostic if connected. "
2756
+ "Open .agenticdome/RECONCILIATION.md, assess or protect the remaining paths, then rerun validate-report. "
2757
+ "This is not passing workload evidence and does not unlock activation."
2758
+ if partial_coverage else
2759
+ "You may import both JSON files as diagnostic evidence, but not as passing evidence. "
2760
+ "Open .agenticdome/RECONCILIATION.md, fix or investigate grouped paths and failing tests, then rerun validate-report."
2761
+ )
2762
+ ),
2633
2763
  }
2634
2764
 
2635
2765
 
@@ -69,8 +69,17 @@ agenticdome validate-report --report AgenticDome_Integration.md
69
69
 
70
70
  This does not edit the application. It checks current source-free structure,
71
71
  requests the tenant-bound semantic assessment and runs detected workload tests.
72
- Import both generated files, `agenticdome-inspection.json` and
73
- `.agenticdome/verification.json`, together in the same onboarding workload.
72
+ The command also writes a local `.agenticdome/RECONCILIATION.md` worklist. If
73
+ it exits non-zero or says `ready=false`, open that file: it groups possible
74
+ bypasses and uncertain internal paths by source file, with the next check for
75
+ each boundary. Review the actual source, fix or investigate those paths, rerun
76
+ the workload tests and validation, and update the canonical AI report. The
77
+ worklist is not uploaded and cannot waive a finding. You may import both
78
+ generated files, `agenticdome-inspection.json` and
79
+ `.agenticdome/verification.json`, together in the same onboarding workload even
80
+ when validation is incomplete. The portal records this as partial evidence and
81
+ can run fixed tenant-policy diagnostics, but it does not grant full verification
82
+ or activation readiness. Rerun and reimport after closing the gaps.
74
83
  Their hashes bind them to the exact Markdown report. A changed report requires
75
84
  rerunning validation. The portal then performs its separate assigned-runtime
76
85
  check; test the actual customer action path before activation. MCP and
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agenticdome-python-sdk"
7
- version = "1.2.38"
7
+ version = "1.2.39"
8
8
  description = "Official Python SDK for AgenticDome security guardrails, DLP, tool authorization, and multi-agent delegation enforcement."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"