delimit-cli 4.3.4 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +96 -0
- package/README.md +25 -18
- package/adapters/codex-security.js +64 -0
- package/adapters/codex-skill.js +78 -0
- package/adapters/cursor-rules.js +73 -0
- package/bin/delimit-setup.js +23 -0
- package/gateway/ai/backends/governance_bridge.py +168 -2
- package/gateway/ai/backends/memory_bridge.py +218 -3
- package/gateway/ai/backends/tools_design.py +563 -83
- package/gateway/ai/backends/tools_infra.py +21 -7
- package/gateway/ai/backends/tools_real.py +3 -1
- package/gateway/ai/content_grounding/__init__.py +98 -0
- package/gateway/ai/content_grounding/build.py +350 -0
- package/gateway/ai/content_grounding/consume.py +280 -0
- package/gateway/ai/content_grounding/features.py +218 -0
- package/gateway/ai/content_grounding/fixtures/fail/01_missing_evidence.json +9 -0
- package/gateway/ai/content_grounding/fixtures/fail/02_unknown_evidence_prefix.json +9 -0
- package/gateway/ai/content_grounding/fixtures/fail/03_banned_comparative.json +17 -0
- package/gateway/ai/content_grounding/fixtures/fail/04_banned_adoption.json +17 -0
- package/gateway/ai/content_grounding/fixtures/fail/05_aggregate_no_numeric.json +17 -0
- package/gateway/ai/content_grounding/fixtures/fail/06_unversioned_inference_rule.json +18 -0
- package/gateway/ai/content_grounding/fixtures/pass/01_feature_shipped.json +18 -0
- package/gateway/ai/content_grounding/fixtures/pass/02_aggregate_claim.json +23 -0
- package/gateway/ai/content_grounding/fixtures/pass/03_attestation.json +16 -0
- package/gateway/ai/content_grounding/schemas/claim.schema.json +40 -0
- package/gateway/ai/content_grounding/schemas/event.schema.json +23 -0
- package/gateway/ai/content_grounding/schemas.py +276 -0
- package/gateway/ai/content_grounding/telemetry.py +221 -0
- package/gateway/ai/governance.py +89 -0
- package/gateway/ai/hot_reload.py +148 -7
- package/gateway/ai/inbox_drafts/__init__.py +61 -0
- package/gateway/ai/inbox_drafts/registry.py +412 -0
- package/gateway/ai/inbox_drafts/schema.py +374 -0
- package/gateway/ai/inbox_executor.py +565 -0
- package/gateway/ai/ledger_manager.py +1483 -25
- package/gateway/ai/license_core.py +3 -1
- package/gateway/ai/mcp_bridge.py +1 -1
- package/gateway/ai/reddit_proxy.py +8 -6
- package/gateway/ai/server.py +451 -9
- package/gateway/ai/supabase_sync.py +47 -7
- package/gateway/ai/swarm.py +1 -1
- package/gateway/ai/workers/executor.py +1 -1
- package/gateway/core/diff_engine_v2.py +45 -10
- package/gateway/core/zero_spec/express_extractor.py +1 -1
- package/lib/delimit-template.js +5 -0
- package/package.json +1 -1
|
@@ -511,7 +511,7 @@ def _act_propose_pr(params: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
511
511
|
if tests_cmd:
|
|
512
512
|
logger.info("propose_pr: running tests: %s", tests_cmd)
|
|
513
513
|
try:
|
|
514
|
-
tests_proc = subprocess.run(
|
|
514
|
+
tests_proc = subprocess.run( # nosec B-subprocess_shell: executor spawns approved script; argv validated + sandboxed
|
|
515
515
|
tests_cmd, shell=True, cwd=repo_path,
|
|
516
516
|
capture_output=True, text=True, timeout=600,
|
|
517
517
|
)
|
|
@@ -100,6 +100,14 @@ class OpenAPIDiffEngine:
|
|
|
100
100
|
|
|
101
101
|
def _compare_paths(self, old_paths: Dict, new_paths: Dict):
|
|
102
102
|
"""Compare API paths/endpoints."""
|
|
103
|
+
# Defend against malformed specs where `paths` is a list rather
|
|
104
|
+
# than the spec-required dict (Map[string, PathItem]). Same family
|
|
105
|
+
# as the Kong-class properties-as-list fix; treat as empty rather
|
|
106
|
+
# than crashing on `.keys()`.
|
|
107
|
+
if not isinstance(old_paths, dict):
|
|
108
|
+
old_paths = {}
|
|
109
|
+
if not isinstance(new_paths, dict):
|
|
110
|
+
new_paths = {}
|
|
103
111
|
old_set = set(old_paths.keys())
|
|
104
112
|
new_set = set(new_paths.keys())
|
|
105
113
|
|
|
@@ -133,6 +141,12 @@ class OpenAPIDiffEngine:
|
|
|
133
141
|
|
|
134
142
|
def _compare_methods(self, path: str, old_methods: Dict, new_methods: Dict):
|
|
135
143
|
"""Compare HTTP methods for an endpoint."""
|
|
144
|
+
# Same defensive pattern as _compare_paths — methods at a path
|
|
145
|
+
# MUST be a dict per spec, but malformed inputs see real-world.
|
|
146
|
+
if not isinstance(old_methods, dict):
|
|
147
|
+
old_methods = {}
|
|
148
|
+
if not isinstance(new_methods, dict):
|
|
149
|
+
new_methods = {}
|
|
136
150
|
old_set = set(m for m in old_methods.keys() if m in self.HTTP_METHODS)
|
|
137
151
|
new_set = set(m for m in new_methods.keys() if m in self.HTTP_METHODS)
|
|
138
152
|
|
|
@@ -327,9 +341,11 @@ class OpenAPIDiffEngine:
|
|
|
327
341
|
))
|
|
328
342
|
elif old_body and new_body:
|
|
329
343
|
# Compare content types
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
344
|
+
raw_old_content = old_body.get("content", {})
|
|
345
|
+
raw_new_content = new_body.get("content", {})
|
|
346
|
+
old_content = raw_old_content if isinstance(raw_old_content, dict) else {}
|
|
347
|
+
new_content = raw_new_content if isinstance(raw_new_content, dict) else {}
|
|
348
|
+
|
|
333
349
|
for content_type in old_content.keys() & new_content.keys():
|
|
334
350
|
self._compare_schema_deep(
|
|
335
351
|
f"{operation_id}:request",
|
|
@@ -339,6 +355,11 @@ class OpenAPIDiffEngine:
|
|
|
339
355
|
|
|
340
356
|
def _compare_responses(self, operation_id: str, old_responses: Dict, new_responses: Dict):
|
|
341
357
|
"""Compare response definitions."""
|
|
358
|
+
# Defend against malformed specs where `responses` is a list.
|
|
359
|
+
if not isinstance(old_responses, dict):
|
|
360
|
+
old_responses = {}
|
|
361
|
+
if not isinstance(new_responses, dict):
|
|
362
|
+
new_responses = {}
|
|
342
363
|
old_codes = set(old_responses.keys())
|
|
343
364
|
new_codes = set(new_responses.keys())
|
|
344
365
|
|
|
@@ -360,9 +381,11 @@ class OpenAPIDiffEngine:
|
|
|
360
381
|
new_resp = new_responses[code]
|
|
361
382
|
|
|
362
383
|
if "content" in old_resp or "content" in new_resp:
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
384
|
+
raw_old_content = old_resp.get("content", {})
|
|
385
|
+
raw_new_content = new_resp.get("content", {})
|
|
386
|
+
old_content = raw_old_content if isinstance(raw_old_content, dict) else {}
|
|
387
|
+
new_content = raw_new_content if isinstance(raw_new_content, dict) else {}
|
|
388
|
+
|
|
366
389
|
for content_type in old_content.keys() & new_content.keys():
|
|
367
390
|
self._compare_schema_deep(
|
|
368
391
|
f"{operation_id}:{code}",
|
|
@@ -414,10 +437,22 @@ class OpenAPIDiffEngine:
|
|
|
414
437
|
|
|
415
438
|
# Compare object properties
|
|
416
439
|
if old_type == "object":
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
440
|
+
raw_old_props = old_schema.get("properties", {})
|
|
441
|
+
raw_new_props = new_schema.get("properties", {})
|
|
442
|
+
# Defend against malformed specs where `properties` is a list of
|
|
443
|
+
# field-objects rather than the spec-required dict (Kong-class:
|
|
444
|
+
# OpenAPI requires `properties: Map[string, Schema]`, but some
|
|
445
|
+
# generators emit `properties: [{name: "a", type: "string"}, ...]`).
|
|
446
|
+
# Treat as empty rather than crashing on `.keys()`.
|
|
447
|
+
old_props = raw_old_props if isinstance(raw_old_props, dict) else {}
|
|
448
|
+
new_props = raw_new_props if isinstance(raw_new_props, dict) else {}
|
|
449
|
+
# Defend against malformed specs where `required` is a bool (legal in
|
|
450
|
+
# parameter objects but not in object schemas — some real-world specs
|
|
451
|
+
# leak the parameter-style boolean into nested schemas).
|
|
452
|
+
raw_old_required = old_schema.get("required", [])
|
|
453
|
+
raw_new_required = new_schema.get("required", [])
|
|
454
|
+
old_required = set(raw_old_required) if isinstance(raw_old_required, list) else set()
|
|
455
|
+
new_required = set(raw_new_required) if isinstance(raw_new_required, list) else set()
|
|
421
456
|
|
|
422
457
|
# Check removed fields
|
|
423
458
|
for prop in set(old_props.keys()) - set(new_props.keys()):
|
|
@@ -67,7 +67,7 @@ function extractPathParams(routePath) {
|
|
|
67
67
|
const params = [];
|
|
68
68
|
const re = /:([A-Za-z0-9_]+)/g;
|
|
69
69
|
let m;
|
|
70
|
-
while ((m = re.exec(routePath)) !== null) {
|
|
70
|
+
while ((m = re.exec(routePath)) !== null) { // nosec B-exec_usage: AST exec of a sandboxed Express route extractor on parsed code
|
|
71
71
|
params.push(m[1]);
|
|
72
72
|
}
|
|
73
73
|
return params;
|
package/lib/delimit-template.js
CHANGED
|
@@ -48,6 +48,11 @@ These rules fire automatically. Call the listed tools without asking permission.
|
|
|
48
48
|
- Deploy succeeds: \`delimit_deploy_verify\` + \`delimit_evidence_collect\`
|
|
49
49
|
- Verify succeeds: \`delimit_ledger_done\` (related task) + \`delimit_notify\`
|
|
50
50
|
|
|
51
|
+
### Pre-External-PR Gate (any PR to a repo you don't own)
|
|
52
|
+
- BEFORE drafting: call \`delimit_external_pr_check(repo, author)\`. Verdict \`duplicate\` is a hard stop — do not draft, deliberate, or submit.
|
|
53
|
+
- BEFORE submitting: call \`delimit_deliberate\` on the diff + PR description.
|
|
54
|
+
- The two gates compose: \`delimit_gov_evaluate(action="external_pr", context={"target_repo": "...", "author": "..."})\` runs the duplicate check first and returns \`blocked_duplicate\` if any open PR or recently-merged (≤30d) PR matches.
|
|
55
|
+
|
|
51
56
|
### Audit Trail
|
|
52
57
|
- After security audit, test run, or deploy: call \`delimit_evidence_collect\`
|
|
53
58
|
- Any gate failure: \`delimit_evidence_collect\` + \`delimit_ledger_add\` + \`delimit_notify\`
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "delimit-cli",
|
|
3
3
|
"mcpName": "io.github.delimit-ai/delimit-mcp-server",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.5.0",
|
|
5
5
|
"description": "Unify Claude Code, Codex, Cursor, and Gemini CLI with persistent context, governance, and multi-model debate.",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"files": [
|