delimit-cli 4.5.13 → 4.6.1

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 (53) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +9 -8
  3. package/bin/delimit-cli.js +179 -4
  4. package/bin/delimit-setup.js +46 -6
  5. package/gateway/ai/_compile_status.py +154 -0
  6. package/gateway/ai/agent_dispatch.py +41 -0
  7. package/gateway/ai/backends/git_health.py +175 -0
  8. package/gateway/ai/backends/tools_infra.py +163 -10
  9. package/gateway/ai/cli_contract.py +185 -0
  10. package/gateway/ai/daemon.py +10 -0
  11. package/gateway/ai/daily_digest.py +1 -2
  12. package/gateway/ai/delimit_daemon.py +67 -0
  13. package/gateway/ai/dispatch_gate.py +399 -0
  14. package/gateway/ai/governance.py +181 -0
  15. package/gateway/ai/heartbeat.py +290 -0
  16. package/gateway/ai/hot_reload.py +1 -2
  17. package/gateway/ai/led193_daemon/executor.py +9 -0
  18. package/gateway/ai/ledger_manager.py +90 -4
  19. package/gateway/ai/ledger_proof.py +127 -0
  20. package/gateway/ai/license.py +132 -47
  21. package/gateway/ai/license_core.cpython-310-x86_64-linux-gnu.so +0 -0
  22. package/gateway/ai/license_core.pyi +1 -1
  23. package/gateway/ai/notify.py +39 -0
  24. package/gateway/ai/outreach_loop_daemon.py +349 -0
  25. package/gateway/ai/outreach_substantive.py +1437 -0
  26. package/gateway/ai/pro_tools.yaml +167 -0
  27. package/gateway/ai/reaper.py +70 -0
  28. package/gateway/ai/reddit_scanner.py +17 -6
  29. package/gateway/ai/sensing/schema.py +1 -1
  30. package/gateway/ai/sensing/signal_store.py +0 -1
  31. package/gateway/ai/server.py +5490 -1602
  32. package/gateway/ai/social_capability/fit_floor.py +114 -12
  33. package/gateway/ai/social_queue.py +166 -10
  34. package/gateway/ai/tdqs_lint.py +611 -0
  35. package/gateway/ai/tenant_auth.py +329 -0
  36. package/gateway/ai/tenant_data.py +339 -0
  37. package/gateway/ai/tenant_paths.py +150 -0
  38. package/gateway/ai/usage_allowlist.py +198 -0
  39. package/gateway/ai/workers/base.py +2 -2
  40. package/gateway/ai/workers/executor.py +32 -3
  41. package/gateway/ai/workers/outreach_drafter.py +0 -1
  42. package/gateway/ai/workers/pr_drafter.py +0 -1
  43. package/gateway/ai/x_ranker.py +12 -2
  44. package/gateway/core/json_schema_diff.py +25 -1
  45. package/lib/auth-signin.js +136 -0
  46. package/lib/auth-signout.js +169 -0
  47. package/lib/delimit-template.js +11 -0
  48. package/lib/migration-2092-banner.js +213 -0
  49. package/package.json +5 -2
  50. package/server.json +4 -4
  51. package/scripts/build-license-core.sh +0 -85
  52. package/scripts/security-check.sh +0 -66
  53. package/scripts/test-license-core-so.sh +0 -107
@@ -0,0 +1,167 @@
1
+ # PRO_TOOLS single source of truth — LED-1410.
2
+ #
3
+ # This file is the canonical list of every Delimit MCP tool that
4
+ # requires a Pro license. Codegen propagates it into:
5
+ #
6
+ # - ai/license_core.py : PRO_TOOLS + FREE_TRIAL_LIMITS (gets
7
+ # compiled to .so for paying customers)
8
+ # - ai/license.py : Python fallback PRO_TOOLS for customers
9
+ # whose platform has no .so, AND the
10
+ # extension frozenset that fills in tools
11
+ # added after the last compiled build
12
+ # - ai/rate_limiter.py : PRO_TOOLS (filtered to non-deliberation
13
+ # tools, since DELIBERATION_TOOLS has its
14
+ # own tier in rate_limiter)
15
+ #
16
+ # To add or remove a Pro tool:
17
+ # 1. Edit this file
18
+ # 2. Run: python3 scripts/gen_pro_tools.py
19
+ # 3. Commit the regenerated .py files alongside the YAML change
20
+ #
21
+ # CI runs scripts/check_pro_tools_drift.py on every push and fails if
22
+ # any of the three generated files diverge from this manifest.
23
+ #
24
+ # DO NOT add an "extension set" in license.py by hand — that was the
25
+ # class of bug that LED-1260 caught (compiled set diverged from
26
+ # fallback set; customers with the binary got tools free while
27
+ # customers without paid). One YAML, three regenerated callers.
28
+
29
+ version: 1
30
+
31
+ # Tools tagged `kind: deliberation` go into the DELIBERATION_TOOLS
32
+ # frozenset in rate_limiter.py (separate rate-limit tier). They are
33
+ # ALSO Pro, but they're not in rate_limiter's PRO_TOOLS frozenset
34
+ # (that one is "pro non-deliberation").
35
+ #
36
+ # `in_compiled` controls whether the tool appears in license_core.py's
37
+ # baked-in PRO_TOOLS set (which gets compiled into the .so customers
38
+ # receive). License.py's fallback + extension sets always include
39
+ # every tool here regardless of in_compiled, so the gate is consistent
40
+ # for non-binary customers. The split exists because:
41
+ # - true → tool is gated by gate_tool() (the function that ships
42
+ # in the .so). Binary customers see this tool as Pro.
43
+ # - false → tool is in the fallback set but NOT in the compiled set.
44
+ # Binary customers' gate_tool() returns None (not gated).
45
+ # Non-binary customers see the tool as Pro via the fallback.
46
+ # The `false` entries are a historical leak — LED-1260 surfaced this
47
+ # class of bug and added the affected tools to the extension/fallback
48
+ # but did NOT close the gate-side gap. The codegen here preserves the
49
+ # current behavior verbatim; closing the gap requires founder
50
+ # ratification per CLAUDE.md "What Constitutes a Breaking Change for
51
+ # Users" (changes default behavior for paying customers). Track at
52
+ # LED-1454 (PR-B follow-up).
53
+ #
54
+ # `free_trial_limit` lets a tool be called N times before requiring a
55
+ # license (used by license_core's FREE_TRIAL_LIMITS dict). Only set
56
+ # this on tools where a free trial makes operational sense; most Pro
57
+ # tools should NOT be partially-free.
58
+
59
+ tools:
60
+ # ── Deliberation (separate rate tier) ────────────────────────────
61
+ - {name: delimit_deliberate, kind: deliberation, free_trial_limit: 3}
62
+ # LED-1260 leaky-gap (in_compiled: false) — landed AFTER the
63
+ # compiled set was last built. LED-1454 ratifies the closure.
64
+ - {name: delimit_security_deliberate, kind: deliberation, in_compiled: false}
65
+
66
+ # ── Governance (deep) ────────────────────────────────────────────
67
+ - {name: delimit_gov_evaluate, kind: pro, category: governance}
68
+ - {name: delimit_gov_policy, kind: pro, category: governance}
69
+ - {name: delimit_gov_run, kind: pro, category: governance}
70
+ - {name: delimit_gov_verify, kind: pro, category: governance}
71
+ # LED-1260 caught this missing from compiled; in_compiled stays false
72
+ # until LED-1454 closes the leaky-gate gap with founder ratification.
73
+ - {name: delimit_gov_new_task, kind: pro, category: governance, in_compiled: false}
74
+
75
+ # ── OS layer ─────────────────────────────────────────────────────
76
+ - {name: delimit_os_plan, kind: pro, category: os}
77
+ - {name: delimit_os_status, kind: pro, category: os}
78
+ - {name: delimit_os_gates, kind: pro, category: os}
79
+
80
+ # ── Deploy pipeline ──────────────────────────────────────────────
81
+ - {name: delimit_deploy_plan, kind: pro, category: deploy}
82
+ - {name: delimit_deploy_build, kind: pro, category: deploy}
83
+ - {name: delimit_deploy_publish, kind: pro, category: deploy}
84
+ - {name: delimit_deploy_verify, kind: pro, category: deploy}
85
+ - {name: delimit_deploy_rollback, kind: pro, category: deploy}
86
+ - {name: delimit_deploy_status, kind: pro, category: deploy}
87
+ - {name: delimit_deploy_site, kind: pro, category: deploy}
88
+ - {name: delimit_deploy_npm, kind: pro, category: deploy}
89
+
90
+ # ── Memory (note: store + recent are FREE; only search is Pro) ───
91
+ - {name: delimit_memory_search, kind: pro, category: memory}
92
+
93
+ # ── Vault ────────────────────────────────────────────────────────
94
+ - {name: delimit_vault_search, kind: pro, category: vault}
95
+ - {name: delimit_vault_snapshot, kind: pro, category: vault}
96
+ - {name: delimit_vault_health, kind: pro, category: vault}
97
+
98
+ # ── Evidence ─────────────────────────────────────────────────────
99
+ - {name: delimit_evidence_collect, kind: pro, category: evidence}
100
+ - {name: delimit_evidence_verify, kind: pro, category: evidence}
101
+
102
+ # ── Models (provider configuration surface) ──────────────────────
103
+ - {name: delimit_models, kind: pro, category: models}
104
+
105
+ # ── Observability ────────────────────────────────────────────────
106
+ - {name: delimit_obs_metrics, kind: pro, category: observability}
107
+ - {name: delimit_obs_logs, kind: pro, category: observability}
108
+ - {name: delimit_obs_status, kind: pro, category: observability}
109
+
110
+ # ── Release ──────────────────────────────────────────────────────
111
+ - {name: delimit_release_plan, kind: pro, category: release}
112
+ - {name: delimit_release_status, kind: pro, category: release}
113
+ - {name: delimit_release_sync, kind: pro, category: release}
114
+
115
+ # ── Cost ─────────────────────────────────────────────────────────
116
+ - {name: delimit_cost_analyze, kind: pro, category: cost}
117
+ - {name: delimit_cost_optimize, kind: pro, category: cost}
118
+ - {name: delimit_cost_alert, kind: pro, category: cost}
119
+
120
+ # ── Social ───────────────────────────────────────────────────────
121
+ - {name: delimit_social_post, kind: pro, category: social}
122
+ - {name: delimit_social_generate, kind: pro, category: social}
123
+ - {name: delimit_social_history, kind: pro, category: social}
124
+ # LED-1260 caught this missing from compiled; in_compiled stays
125
+ # false until LED-1454 closes the leaky-gate gap.
126
+ - {name: delimit_social_approve, kind: pro, category: social, in_compiled: false}
127
+
128
+ # ── Security orchestrator ────────────────────────────────────────
129
+ # LED-1260 leaky-gap (in_compiled: false) — see LED-1454.
130
+ - {name: delimit_security_ingest, kind: pro, category: security, in_compiled: false}
131
+
132
+ # ── Repo deep ────────────────────────────────────────────────────
133
+ # LED-1260 leaky-gap on all four repo_* tools (in_compiled: false) —
134
+ # binary customers got these tools free while non-binary customers
135
+ # paid via the fallback set. LED-1454 ratifies the closure.
136
+ - {name: delimit_repo_analyze, kind: pro, category: repo, in_compiled: false}
137
+ - {name: delimit_repo_config_audit, kind: pro, category: repo, in_compiled: false}
138
+ - {name: delimit_repo_config_validate, kind: pro, category: repo, in_compiled: false}
139
+ - {name: delimit_repo_diagnose, kind: pro, category: repo, in_compiled: false}
140
+
141
+ # ── Test ─────────────────────────────────────────────────────────
142
+ # LED-1260 leaky-gap (in_compiled: false) — see LED-1454.
143
+ - {name: delimit_test_coverage, kind: pro, category: test, in_compiled: false}
144
+
145
+ # ── Screen capture (browser automation) ──────────────────────────
146
+ - {name: delimit_screen_record, kind: pro, category: capture}
147
+ - {name: delimit_screenshot, kind: pro, category: capture}
148
+
149
+ # ── Notifications ────────────────────────────────────────────────
150
+ - {name: delimit_notify, kind: pro, category: notify}
151
+
152
+ # ── Agent orchestration ──────────────────────────────────────────
153
+ - {name: delimit_agent_dispatch, kind: pro, category: agent}
154
+ - {name: delimit_agent_status, kind: pro, category: agent}
155
+ - {name: delimit_agent_complete, kind: pro, category: agent}
156
+ - {name: delimit_agent_handoff, kind: pro, category: agent}
157
+
158
+ # ── Autonomous build loop ────────────────────────────────────────
159
+ # LED-1260 leaky-gap on all four (in_compiled: false) — these landed
160
+ # AFTER the compiled set was last built. LED-1454 ratifies closure.
161
+ - {name: delimit_next_task, kind: pro, category: build_loop, in_compiled: false}
162
+ - {name: delimit_task_complete, kind: pro, category: build_loop, in_compiled: false}
163
+ - {name: delimit_loop_status, kind: pro, category: build_loop, in_compiled: false}
164
+ - {name: delimit_loop_config, kind: pro, category: build_loop, in_compiled: false}
165
+
166
+ # ── Worker Pool v2 executor (LED-981) ────────────────────────────
167
+ - {name: delimit_executor, kind: pro, category: agent}
@@ -0,0 +1,70 @@
1
+ import subprocess
2
+ import logging
3
+ import json
4
+ import time
5
+ import os
6
+ import re
7
+ from pathlib import Path
8
+ from typing import Dict, Any, List
9
+
10
+ logger = logging.getLogger("delimit.reaper")
11
+
12
+ def reap_agent_tasks(project_path: str = "/home/delimit/delimit-gateway") -> List[str]:
13
+ """Reap completed agent arms by checking for commits and merging."""
14
+ from ai.agent_dispatch import get_agent_status, complete_task
15
+ from ai.ledger_manager import update_item
16
+
17
+ # 1. Get all dispatched tasks
18
+ res = get_agent_status()
19
+ tasks = res.get("tasks", [])
20
+ if not isinstance(tasks, list):
21
+ tasks = list(tasks.values())
22
+
23
+ reaped = []
24
+ for task in tasks:
25
+ if task.get("status") != "dispatched":
26
+ continue
27
+
28
+ task_id = task.get("id")
29
+ desc = task.get("description", "")
30
+
31
+ # 2. Extract branch from description
32
+ match = re.search(r'branch ([\w/-]+)', desc)
33
+ if not match:
34
+ continue
35
+ branch = match.group(1)
36
+
37
+ # 3. Check for new commits on the branch
38
+ try:
39
+ cmd = ["git", "-C", project_path, "log", f"main..${branch}", "--oneline"]
40
+ p = subprocess.run(cmd, capture_output=True, text=True)
41
+ if p.returncode == 0 and p.stdout.strip():
42
+ logger.info(f"Reaper: Found activity on ${branch} for task ${task_id}")
43
+
44
+ # 4. RUN TESTS
45
+ test_cmd = ["python3", "-m", "pytest", "tests/", "-v", "--maxfail=3"]
46
+ env = {"DELIMIT_TEST_MODE": "1", "PATH": f"/root/.delimit/shims:${os.environ['PATH']}"}
47
+ test_p = subprocess.run(test_cmd, cwd=project_path, capture_output=True, text=True, env={**os.environ, **env})
48
+
49
+ if test_p.returncode == 0:
50
+ # 5. MERGE
51
+ subprocess.run(["git", "-C", project_path, "checkout", "main"], check=True)
52
+ subprocess.run(["git", "-C", project_path, "merge", branch], check=True)
53
+
54
+ # 6. COMPLETE
55
+ complete_task(task_id, result="Reaper: Actually built and merged to main.")
56
+
57
+ # 7. Update Ledger
58
+ ctx = task.get("context", "")
59
+ led_match = re.search(r'LED-(\d+)', ctx)
60
+ if led_match:
61
+ led_id = led_match.group(0)
62
+ update_item(item_id=led_id, status="done", note=f"Arm ${task_id} merged.")
63
+
64
+ reaped.append(task_id)
65
+ else:
66
+ logger.warning(f"Reaper: Tests failed for ${branch}, skipping merge.")
67
+ except Exception as e:
68
+ logger.warning(f"Reaper: Failed to reap task ${task_id}: ${e}")
69
+
70
+ return reaped
@@ -470,14 +470,19 @@ def score_and_classify(
470
470
  final_score = engagement * fresh_mult * comment_opp * relevance_mult
471
471
  is_karma = group == "karma_building"
472
472
 
473
- # Classification
474
- if post.get("stickied") or age_h > 48 or comments > 100:
473
+ # Classification (LED-1335 freshness tightening 2026-05-12: founder
474
+ # explicit "we want fresh posts for highest visibility." Comment
475
+ # visibility decays sharply after 6h, marginal after 12h, ~zero
476
+ # after 24h. Old behavior let 12-48h posts through as medium and
477
+ # the daemon drafted on them. New thresholds: hard-skip > 24h,
478
+ # high_priority < 6h, medium_priority < 12h, low_priority < 24h.)
479
+ if post.get("stickied") or age_h > 24 or comments > 100:
475
480
  priority = "skip"
476
- elif final_score >= 30 and age_h < 12 and comments < 50:
481
+ elif final_score >= 30 and age_h < 6 and comments < 50:
477
482
  priority = "high_priority"
478
- elif final_score >= 10 or (len(tags) >= 2 and age_h < 24):
483
+ elif (final_score >= 10 and age_h < 12) or (len(tags) >= 2 and age_h < 12):
479
484
  priority = "medium_priority"
480
- elif final_score >= 3:
485
+ elif final_score >= 3 and age_h < 24:
481
486
  priority = "low_priority"
482
487
  else:
483
488
  priority = "skip"
@@ -724,6 +729,7 @@ def monitor_user_engagement(username: str = "delimitdev", proxy_url: str = PROXY
724
729
  cd = cc.get("data", {})
725
730
  if cd.get("id") != comment_id:
726
731
  continue
732
+ our_comment_body = cd.get("body") or ""
727
733
  replies_listing = cd.get("replies")
728
734
  if not isinstance(replies_listing, dict):
729
735
  continue
@@ -732,7 +738,10 @@ def monitor_user_engagement(username: str = "delimitdev", proxy_url: str = PROXY
732
738
  rd = rc.get("data", {})
733
739
  reply_id = rd.get("id", "")
734
740
  reply_author = rd.get("author", "")
735
- reply_body = (rd.get("body") or "")[:200]
741
+ # Capture full reply body for the follow-up drafter (the alert
742
+ # email's short preview still uses the truncated form below).
743
+ reply_body_full = rd.get("body") or ""
744
+ reply_body = reply_body_full[:200]
736
745
  if not reply_id or reply_author == username:
737
746
  continue
738
747
  seen_key = f"reply_{reply_id}"
@@ -744,6 +753,8 @@ def monitor_user_engagement(username: str = "delimitdev", proxy_url: str = PROXY
744
753
  "reply_id": reply_id,
745
754
  "reply_author": reply_author,
746
755
  "reply_body": reply_body,
756
+ "reply_body_full": reply_body_full,
757
+ "our_comment_body": our_comment_body,
747
758
  "subreddit": d.get("subreddit", ""),
748
759
  "title": d.get("link_title", ""),
749
760
  "message": f"u/{reply_author} replied to your comment in r/{d.get('subreddit','')}: {reply_body[:100]}",
@@ -10,7 +10,7 @@ from __future__ import annotations
10
10
  import hashlib
11
11
  import re
12
12
  from dataclasses import dataclass, field, asdict
13
- from typing import Any, Dict, List, Optional
13
+ from typing import Any, Dict, List
14
14
  from urllib.parse import urlparse, urlunparse, parse_qsl, urlencode
15
15
 
16
16
 
@@ -12,7 +12,6 @@ from __future__ import annotations
12
12
 
13
13
  import json
14
14
  import os
15
- import time
16
15
  import uuid
17
16
  from collections import Counter, defaultdict
18
17
  from datetime import datetime, timedelta, timezone