semanticembed 0.7.4__tar.gz → 0.7.5__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 (129) hide show
  1. {semanticembed-0.7.4 → semanticembed-0.7.5}/.claude/skills/analyze/SKILL.md +1 -1
  2. semanticembed-0.7.5/.github/CODEOWNERS +5 -0
  3. semanticembed-0.7.5/.github/dependabot.yml +21 -0
  4. {semanticembed-0.7.4 → semanticembed-0.7.5}/.github/workflows/ci.yml +11 -4
  5. semanticembed-0.7.5/.github/workflows/corpus.yml +42 -0
  6. semanticembed-0.7.5/.github/workflows/jev-drift.yml +40 -0
  7. semanticembed-0.7.5/.github/workflows/leak-guard.yml +88 -0
  8. semanticembed-0.7.5/.github/workflows/publish.yml +112 -0
  9. {semanticembed-0.7.4 → semanticembed-0.7.5}/CHANGELOG.md +66 -2
  10. {semanticembed-0.7.4 → semanticembed-0.7.5}/PKG-INFO +54 -24
  11. {semanticembed-0.7.4 → semanticembed-0.7.5}/README.md +50 -20
  12. semanticembed-0.7.5/corpus/manifest.json +408 -0
  13. {semanticembed-0.7.4 → semanticembed-0.7.5}/docs/api_reference.md +24 -6
  14. {semanticembed-0.7.4 → semanticembed-0.7.5}/docs/dimensions.md +5 -6
  15. {semanticembed-0.7.4 → semanticembed-0.7.5}/docs/getting_started.md +18 -11
  16. semanticembed-0.7.5/docs/jev.md +122 -0
  17. {semanticembed-0.7.4 → semanticembed-0.7.5}/docs/output_format.md +1 -1
  18. semanticembed-0.7.5/docs/scoring_gaps.md +52 -0
  19. {semanticembed-0.7.4 → semanticembed-0.7.5}/docs/validation_methodology.md +13 -18
  20. {semanticembed-0.7.4 → semanticembed-0.7.5}/pyproject.toml +5 -3
  21. semanticembed-0.7.5/scripts/corpus_score.py +243 -0
  22. semanticembed-0.7.5/scripts/jev/canary_baseline.json +173 -0
  23. semanticembed-0.7.5/scripts/jev/canary_cases.json +54 -0
  24. semanticembed-0.7.5/scripts/jev/drift_canary.py +160 -0
  25. semanticembed-0.7.5/scripts/jev/edge_audit.py +193 -0
  26. semanticembed-0.7.5/scripts/jev/jev_client.py +108 -0
  27. semanticembed-0.7.5/scripts/jev/leak_guard.py +280 -0
  28. semanticembed-0.7.5/scripts/jev/questions.py +135 -0
  29. {semanticembed-0.7.4 → semanticembed-0.7.5}/skill/SKILL.md +0 -1
  30. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/__init__.py +1 -1
  31. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/agent/README.md +1 -1
  32. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/agent/agent.py +61 -17
  33. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/client.py +1 -1
  34. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/explain.py +1 -1
  35. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/extract.py +1177 -167
  36. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/find_edges.py +154 -3
  37. semanticembed-0.7.5/tests/conftest.py +15 -0
  38. semanticembed-0.7.5/tests/fixtures/crew_decorator/config/tasks.yaml +7 -0
  39. semanticembed-0.7.5/tests/fixtures/crew_decorator/crew.py +36 -0
  40. semanticembed-0.7.5/tests/fixtures/langgraph_command.py +42 -0
  41. semanticembed-0.7.5/tests/test_agent_isolation.py +128 -0
  42. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/test_client.py +42 -0
  43. semanticembed-0.7.5/tests/test_corpus_score.py +59 -0
  44. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/test_extract.py +24 -0
  45. semanticembed-0.7.5/tests/test_extract_gaps.py +319 -0
  46. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/test_extract_iac.py +22 -2
  47. semanticembed-0.7.5/tests/test_extract_ported.py +181 -0
  48. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/test_extract_regressions.py +24 -0
  49. semanticembed-0.7.5/tests/test_jev_scripts.py +212 -0
  50. semanticembed-0.7.5/tests/test_security_paths.py +189 -0
  51. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/test_skill.py +5 -4
  52. semanticembed-0.7.5/tests/test_skill_permissions.py +33 -0
  53. semanticembed-0.7.4/.github/workflows/publish.yml +0 -51
  54. {semanticembed-0.7.4 → semanticembed-0.7.5}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  55. {semanticembed-0.7.4 → semanticembed-0.7.5}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  56. {semanticembed-0.7.4 → semanticembed-0.7.5}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  57. {semanticembed-0.7.4 → semanticembed-0.7.5}/.gitignore +0 -0
  58. {semanticembed-0.7.4 → semanticembed-0.7.5}/CODE_OF_CONDUCT.md +0 -0
  59. {semanticembed-0.7.4 → semanticembed-0.7.5}/CONTRIBUTING.md +0 -0
  60. {semanticembed-0.7.4 → semanticembed-0.7.5}/LICENSE +0 -0
  61. {semanticembed-0.7.4 → semanticembed-0.7.5}/LICENSE-FAQ.md +0 -0
  62. {semanticembed-0.7.4 → semanticembed-0.7.5}/SECURITY.md +0 -0
  63. {semanticembed-0.7.4 → semanticembed-0.7.5}/docs/input_format.md +0 -0
  64. {semanticembed-0.7.4 → semanticembed-0.7.5}/docs/license_keys.md +0 -0
  65. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/README.md +0 -0
  66. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/ai_agent_pipeline.json +0 -0
  67. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/autogen_codereview.py +0 -0
  68. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/cicd_pipeline.json +0 -0
  69. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/crewai_content_pipeline.py +0 -0
  70. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/google_online_boutique.json +0 -0
  71. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/langgraph_research_agent.py +0 -0
  72. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/react/README.md +0 -0
  73. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/react/RadarChart.tsx +0 -0
  74. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/react/RiskTable.tsx +0 -0
  75. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/react/TopologySummary.tsx +0 -0
  76. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/react/useSemanticEmbed.ts +0 -0
  77. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/sample_pipeline.json +0 -0
  78. {semanticembed-0.7.4 → semanticembed-0.7.5}/examples/weaveworks_sock_shop.json +0 -0
  79. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/.gitignore +0 -0
  80. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/DEPLOY.md +0 -0
  81. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/README.md +0 -0
  82. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/app.py +0 -0
  83. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/build.sh +0 -0
  84. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/examples/autogen_codereview.py +0 -0
  85. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/examples/autogen_codereview_after.py +0 -0
  86. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/examples/boutique.json +0 -0
  87. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/examples/boutique_after.json +0 -0
  88. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/examples/crewai_content.py +0 -0
  89. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/examples/crewai_content_after.py +0 -0
  90. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/examples/langgraph_research.py +0 -0
  91. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/examples/langgraph_research_after.py +0 -0
  92. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/requirements.txt +0 -0
  93. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/smoke_test_phase_b.py +0 -0
  94. {semanticembed-0.7.4 → semanticembed-0.7.5}/huggingface/smoke_test_phase_c.py +0 -0
  95. {semanticembed-0.7.4 → semanticembed-0.7.5}/notebooks/01_quickstart.ipynb +0 -0
  96. {semanticembed-0.7.4 → semanticembed-0.7.5}/notebooks/02_dimensions.ipynb +0 -0
  97. {semanticembed-0.7.4 → semanticembed-0.7.5}/notebooks/03_drift_detection.ipynb +0 -0
  98. {semanticembed-0.7.4 → semanticembed-0.7.5}/notebooks/04_bring_your_own.ipynb +0 -0
  99. {semanticembed-0.7.4 → semanticembed-0.7.5}/notebooks/05_ai_agent_pipelines.ipynb +0 -0
  100. {semanticembed-0.7.4 → semanticembed-0.7.5}/notebooks/06_cicd_pipelines.ipynb +0 -0
  101. {semanticembed-0.7.4 → semanticembed-0.7.5}/notebooks/07_opentelemetry.ipynb +0 -0
  102. {semanticembed-0.7.4 → semanticembed-0.7.5}/notebooks/08_qwen_compression.ipynb +0 -0
  103. {semanticembed-0.7.4 → semanticembed-0.7.5}/notebooks/09_quantum_reachability.ipynb +0 -0
  104. {semanticembed-0.7.4 → semanticembed-0.7.5}/notebooks/qwen_compression_cookbook.py +0 -0
  105. {semanticembed-0.7.4 → semanticembed-0.7.5}/skill/SETUP.md +0 -0
  106. {semanticembed-0.7.4 → semanticembed-0.7.5}/skill/analyze.py +0 -0
  107. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/agent/__init__.py +0 -0
  108. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/agent/__main__.py +0 -0
  109. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/agent/gemini_agent.py +0 -0
  110. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/dedupe.py +0 -0
  111. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/exceptions.py +0 -0
  112. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/live.py +0 -0
  113. {semanticembed-0.7.4 → semanticembed-0.7.5}/src/semanticembed/models.py +0 -0
  114. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/__init__.py +0 -0
  115. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/fixtures/autogen_app.py +0 -0
  116. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/fixtures/cdk_app.py +0 -0
  117. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/fixtures/crewai_app.py +0 -0
  118. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/fixtures/docker-compose.yml +0 -0
  119. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/fixtures/langgraph_app.py +0 -0
  120. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/fixtures/pulumi_app.py +0 -0
  121. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/fixtures/stack.yaml +0 -0
  122. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/fixtures/trace_jaeger.json +0 -0
  123. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/fixtures/trace_otlp.json +0 -0
  124. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/fixtures/trace_zipkin.json +0 -0
  125. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/test_aclient.py +0 -0
  126. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/test_dedupe.py +0 -0
  127. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/test_extract_ai.py +0 -0
  128. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/test_extract_otel.py +0 -0
  129. {semanticembed-0.7.4 → semanticembed-0.7.5}/tests/test_live.py +0 -0
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: analyze
3
3
  description: Scan a repo for infrastructure files (docker-compose, k8s, terraform, GitHub Actions), extract edges, and run 6D structural risk analysis via SemanticEmbed.
4
- allowed-tools: Bash Read Glob
4
+ allowed-tools: Read Glob
5
5
  ---
6
6
 
7
7
  # /analyze — Structural Risk Analysis
@@ -0,0 +1,5 @@
1
+ # Changes to these paths need review from the owner once main has a ruleset
2
+ # that requires code-owner review.
3
+ * @jmurray10
4
+ /.github/ @jmurray10
5
+ /src/semanticembed/ @jmurray10
@@ -0,0 +1,21 @@
1
+ # Weekly update PRs for the pinned GitHub Actions and the Python dependencies.
2
+ version: 2
3
+ updates:
4
+ - package-ecosystem: github-actions
5
+ directory: /
6
+ schedule:
7
+ interval: weekly
8
+ groups:
9
+ actions:
10
+ patterns: ["*"]
11
+ - package-ecosystem: pip
12
+ directory: /
13
+ schedule:
14
+ interval: weekly
15
+ groups:
16
+ python:
17
+ patterns: ["*"]
18
+ - package-ecosystem: pip
19
+ directory: /huggingface
20
+ schedule:
21
+ interval: weekly
@@ -6,6 +6,9 @@ on:
6
6
  pull_request:
7
7
  branches: [main]
8
8
 
9
+ permissions:
10
+ contents: read
11
+
9
12
  jobs:
10
13
  test:
11
14
  name: Test (${{ matrix.python-version }} on ${{ matrix.os }})
@@ -23,10 +26,12 @@ jobs:
23
26
  python-version: "3.12"
24
27
 
25
28
  steps:
26
- - uses: actions/checkout@v4
29
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
30
+ with:
31
+ persist-credentials: false
27
32
 
28
33
  - name: Set up Python ${{ matrix.python-version }}
29
- uses: actions/setup-python@v5
34
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
30
35
  with:
31
36
  python-version: ${{ matrix.python-version }}
32
37
  cache: pip
@@ -43,9 +48,11 @@ jobs:
43
48
  name: Build wheel + sdist
44
49
  runs-on: ubuntu-latest
45
50
  steps:
46
- - uses: actions/checkout@v4
51
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
52
+ with:
53
+ persist-credentials: false
47
54
 
48
- - uses: actions/setup-python@v5
55
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
49
56
  with:
50
57
  python-version: "3.12"
51
58
 
@@ -0,0 +1,42 @@
1
+ name: Corpus
2
+
3
+ # Manual only. Cloning the pinned public repos is not part of every PR.
4
+ on:
5
+ workflow_dispatch:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ score:
12
+ name: Score pinned corpus
13
+ runs-on: ubuntu-latest
14
+ environment: jev # only jobs in this environment can read TYPESAFE_API_KEY
15
+ steps:
16
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
17
+ with:
18
+ persist-credentials: false
19
+
20
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
21
+ with:
22
+ python-version: "3.12"
23
+
24
+ - name: Install
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install -e '.[dev,extract]'
28
+
29
+ - name: Score tuning and held-out extraction
30
+ run: python scripts/corpus_score.py --split all
31
+
32
+ # Report only: Jev judges every extracted edge, including edges that
33
+ # appear only at each repo's current HEAD. Skipped without the secret.
34
+ - name: Jev edge audit
35
+ env:
36
+ TYPESAFE_API_KEY: ${{ secrets.TYPESAFE_API_KEY }}
37
+ run: |
38
+ if [ -z "$TYPESAFE_API_KEY" ]; then
39
+ echo "::notice::TYPESAFE_API_KEY is not set; the Jev edge audit was not run."
40
+ exit 0
41
+ fi
42
+ python scripts/jev/edge_audit.py --log edge_audit_log.jsonl
@@ -0,0 +1,40 @@
1
+ name: Jev drift
2
+
3
+ # Weekly check that the Jev model behind the leak guard still answers the
4
+ # fixed cases in scripts/jev/canary_cases.json the way it did when the
5
+ # guard was measured (scripts/jev/canary_baseline.json). Also reports, without
6
+ # failing, how the moving alias jev-latest answers, so a new model release
7
+ # shows up before the pinned version is changed.
8
+ #
9
+ # Needs the secret TYPESAFE_API_KEY in the `jev` environment. About 48 requests a run.
10
+
11
+ on:
12
+ schedule:
13
+ - cron: "17 6 * * 1"
14
+ workflow_dispatch:
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ drift:
21
+ name: Compare with baseline
22
+ runs-on: ubuntu-latest
23
+ environment: jev # only jobs in this environment can read TYPESAFE_API_KEY
24
+ timeout-minutes: 10
25
+ steps:
26
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
27
+ with:
28
+ persist-credentials: false
29
+
30
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
31
+ with:
32
+ python-version: "3.12"
33
+
34
+ - name: Install
35
+ run: python -m pip install "httpx==0.28.1"
36
+
37
+ - name: Run the drift check
38
+ env:
39
+ TYPESAFE_API_KEY: ${{ secrets.TYPESAFE_API_KEY }}
40
+ run: python -I scripts/jev/drift_canary.py --also jev-latest
@@ -0,0 +1,88 @@
1
+ name: Leak guard
2
+
3
+ # Checks the lines a pull request adds for internals of the hosted service:
4
+ # a risk flag's cutoff, the steps that compute a value, how the input graph
5
+ # is changed before scoring, or how ties are broken. Uses TypeSafe's Jev
6
+ # model (scripts/jev/leak_guard.py, measurements in docs/jev.md).
7
+ #
8
+ # Needs the secret TYPESAFE_API_KEY in the `jev` environment. Without it the check fails
9
+ # rather than passing unchecked. Pull requests from forks get no secrets, so
10
+ # they need a maintainer's review for this instead.
11
+ #
12
+ # The guard's code is taken from the base branch, never from the pull
13
+ # request, so a pull request cannot change what runs with the key.
14
+
15
+ on:
16
+ pull_request:
17
+ branches: [main]
18
+
19
+ permissions:
20
+ contents: read
21
+
22
+ concurrency:
23
+ group: leak-guard-${{ github.event.pull_request.number }}
24
+ cancel-in-progress: true
25
+
26
+ jobs:
27
+ leak-guard:
28
+ name: Check added lines
29
+ runs-on: ubuntu-latest
30
+ environment: jev # only jobs in this environment can read TYPESAFE_API_KEY
31
+ timeout-minutes: 15
32
+ steps:
33
+ - name: Check out the pull request (data only)
34
+ uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
35
+ with:
36
+ ref: ${{ github.event.pull_request.head.sha }}
37
+ fetch-depth: 0
38
+ path: pr
39
+ persist-credentials: false
40
+
41
+ - name: Check out the guard from the base branch
42
+ uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
43
+ with:
44
+ ref: ${{ github.event.pull_request.base.sha }}
45
+ path: trusted
46
+ persist-credentials: false
47
+
48
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
49
+ with:
50
+ python-version: "3.12"
51
+
52
+ - name: Install the guard's one dependency
53
+ run: python -m pip install "httpx==0.28.1"
54
+
55
+ - name: Run the leak guard
56
+ env:
57
+ TYPESAFE_API_KEY: ${{ secrets.TYPESAFE_API_KEY }}
58
+ BASE_SHA: ${{ github.event.pull_request.base.sha }}
59
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
60
+ FROM_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
61
+ ACTOR: ${{ github.event.pull_request.user.login }}
62
+ run: |
63
+ if [ ! -f trusted/scripts/jev/leak_guard.py ]; then
64
+ echo "::notice::The leak guard is not on the base branch yet. It checks pull requests once it is merged."
65
+ exit 0
66
+ fi
67
+ if [ -z "$TYPESAFE_API_KEY" ] && [ "$FROM_FORK" = "true" ]; then
68
+ echo "::error::Pull requests from forks get no secrets, so the leak guard cannot run. A maintainer must review the added lines."
69
+ exit 2
70
+ fi
71
+ if [ -z "$TYPESAFE_API_KEY" ] && [ "$ACTOR" = "dependabot[bot]" ]; then
72
+ echo "::notice::Dependabot pull requests get no repository secrets. Leak guard not run; review the version bumps by hand."
73
+ exit 0
74
+ fi
75
+ cd trusted
76
+ python -I scripts/jev/leak_guard.py --repo ../pr --base "$BASE_SHA" --head "$HEAD_SHA" \
77
+ --log ../leak_guard_log.jsonl --json-out ../leak_guard_results.json
78
+
79
+ - name: Keep the requests and answers
80
+ if: always()
81
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
82
+ with:
83
+ name: leak-guard-log
84
+ path: |
85
+ leak_guard_log.jsonl
86
+ leak_guard_results.json
87
+ if-no-files-found: ignore
88
+ retention-days: 30
@@ -0,0 +1,112 @@
1
+ name: Publish to PyPI
2
+
3
+ # Runs on a tag push of the form `v*`: git tag v0.7.5 && git push origin v0.7.5
4
+ # Uses PyPI Trusted Publishing (OIDC), so no PyPI token is stored in GitHub.
5
+ #
6
+ # A release is published only when all of these hold:
7
+ # 1. the tagged commit is on main (a tag on an unreviewed branch is refused);
8
+ # 2. the tag matches pyproject.toml and semanticembed.__version__;
9
+ # 3. the full test suite passes on that commit;
10
+ # 4. someone approves the `pypi` environment. Set required reviewers in
11
+ # Settings -> Environments -> pypi, and restrict it to `v*` tags.
12
+ #
13
+ # One-time PyPI setup: pypi.org -> Account -> Publishing -> trusted publisher
14
+ # Project: semanticembed Owner: jmurray10 Repository: semanticembed-sdk
15
+ # Workflow: publish.yml Environment: pypi
16
+ #
17
+ # Actions are pinned to commit SHAs; Dependabot proposes updates.
18
+
19
+ on:
20
+ push:
21
+ tags:
22
+ - "v*"
23
+
24
+ permissions:
25
+ contents: read
26
+
27
+ concurrency:
28
+ group: publish
29
+ cancel-in-progress: false
30
+
31
+ jobs:
32
+ verify:
33
+ name: Verify tag and run tests
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
37
+ with:
38
+ fetch-depth: 0
39
+ persist-credentials: false
40
+
41
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
42
+ with:
43
+ python-version: "3.12"
44
+
45
+ - name: Tagged commit is on main
46
+ run: |
47
+ git fetch --no-tags origin main
48
+ if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
49
+ echo "::error::Tag $GITHUB_REF_NAME points at a commit that is not on main"
50
+ exit 1
51
+ fi
52
+
53
+ - name: Tag matches the package version
54
+ run: |
55
+ python - <<'EOF'
56
+ import os, pathlib, re, sys, tomllib
57
+ tag = os.environ["GITHUB_REF_NAME"].removeprefix("v")
58
+ pkg = tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"]
59
+ init = re.search(r"__version__\s*=\s*[\"']([^\"']+)",
60
+ pathlib.Path("src/semanticembed/__init__.py").read_text()).group(1)
61
+ if not tag == pkg == init:
62
+ sys.exit(f"::error::tag {tag}, pyproject.toml {pkg} and __version__ {init} must match")
63
+ print(f"version {tag}")
64
+ EOF
65
+
66
+ - name: Install
67
+ run: |
68
+ python -m pip install --upgrade pip
69
+ pip install -e '.[dev,extract]'
70
+
71
+ - name: Run pytest
72
+ run: pytest -q
73
+
74
+ build:
75
+ name: Build sdist + wheel
76
+ needs: verify
77
+ runs-on: ubuntu-latest
78
+ steps:
79
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
80
+ with:
81
+ persist-credentials: false
82
+
83
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
84
+ with:
85
+ python-version: "3.12"
86
+
87
+ - name: Build
88
+ run: |
89
+ python -m pip install --upgrade build twine
90
+ python -m build
91
+ python -m twine check dist/*
92
+
93
+ - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
94
+ with:
95
+ name: dist
96
+ path: dist/
97
+ if-no-files-found: error
98
+
99
+ publish:
100
+ name: Publish
101
+ needs: build
102
+ runs-on: ubuntu-latest
103
+ environment: pypi
104
+ permissions:
105
+ id-token: write # trusted publishing; nothing else in this job can write
106
+ steps:
107
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
108
+ with:
109
+ name: dist
110
+ path: dist/
111
+
112
+ - uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
@@ -2,13 +2,77 @@
2
2
 
3
3
  All notable changes to the SemanticEmbed SDK.
4
4
 
5
+ ## 0.7.5 — Security fixes, parser corrections
6
+
7
+ ### Security
8
+
9
+ - **Agent no longer runs the scanned project's hooks.** `semanticembed-agent`
10
+ started the Claude Code CLI in your working directory with default
11
+ settings, so a cloned repo's `.claude/settings.json` hooks ran shell
12
+ commands with no prompt. The agent now loads no settings files, runs the
13
+ CLI in an empty temporary directory, and has no built-in tools (shell,
14
+ file read/write, web fetch). Requires `claude-agent-sdk>=0.1.60`; older
15
+ versions ignore `setting_sources=[]` and are refused at startup.
16
+ - **`extract.from_requirements`** follows `-r` only to files inside the
17
+ starting file's directory, after resolving symlinks. Lines of a file
18
+ outside it used to become node names sent to the API.
19
+ - **`find_edges()` LLM fallback** skips `node_modules`, `.venv`, `vendor`
20
+ and similar directories, skips files that resolve outside the scanned
21
+ path, and replaces secret-looking values with `[REDACTED]` before
22
+ sending file text to Anthropic or Google. README "What gets sent where"
23
+ now covers `find_edges()` and `explain()` / `ask()`.
24
+ - **Skills** no longer pre-approve shell commands.
25
+ - **Releases** publish only from a tag on `main` that matches the package
26
+ version, after the test suite passes, with approval on the `pypi`
27
+ environment. Workflow actions are pinned to commit SHAs; Dependabot is on.
28
+
29
+ ### Changed
30
+
31
+ - **`extract.from_crewai`** reads `@CrewBase` / `@agent` / `@task` / `@crew`
32
+ crews: agent-to-task edges, the sequential task chain, YAML and inline
33
+ `context` dependencies, and `manager_agent` fan-out. crewAI-examples
34
+ `stock_analysis` goes from 0 to 7 edges and `surprise_trip` from 0 to 5.
35
+ - **`extract.from_langgraph`** reads `Command(goto=...)`,
36
+ `Command[Literal[...]]`, and `Send(...)`, and keeps a separate `START` /
37
+ `END` per `StateGraph`. A compiled subgraph mounted with `add_node`, or
38
+ invoked with `ainvoke` / `invoke` / `stream` / `astream`, is wired to the
39
+ caller. `open_deep_research` `deep_researcher.py` goes from 6 edges (three
40
+ graphs sharing one `START`) to 16.
41
+ - **`extract.from_terraform`** labels a resource `type.name` when its short
42
+ name is shared (`aws_vpc.this` vs `aws_internet_gateway.this`). On
43
+ terraform-aws-vpc this is 55 edges / 35 nodes with a `this` hub, then
44
+ 119 edges / 96 nodes with no bare `this` once module blocks are included.
45
+ Correct keys put real modules over the free-tier 50-node cap.
46
+ - **`extract.from_directory`** takes `include` and `exclude` source labels.
47
+ The default still parses every recognized source, including GitHub Actions
48
+ and Python imports.
49
+ - **Kubernetes names that collide** (`Service` and `Deployment` both called
50
+ `frontend`) are labeled `Kind/name`. Same-name pairs used to become
51
+ self-loops and disappear. `GoogleCloudPlatform/microservices-demo`
52
+ `c7cb6d5` `release/` is 28 edges after that, not 17.
53
+ - **Pulumi / CDK kwargs** such as `vpc_security_group_ids=[group.id]` count
54
+ as a reference to `group`.
55
+ - **Python `from ._native import`** is resolved against the importing package.
56
+ - **Corpus check** (`corpus/manifest.json`, `scripts/corpus_score.py`):
57
+ held-out extraction precision 1.0 and recall 1.0 on 2026-11-04
58
+ (55/55 edges, 6 files). Tuning was 1.0 (59/59). The only pin behind
59
+ HEAD was this repo, and those labeled files did not change edges.
60
+ Hosted scoring notes are in `docs/scoring_gaps.md`. The 0.7.4 changelog line "vpc goes
61
+ from 2 to 56 edges" does not reproduce on vpc `b3abd6df`: the indexed-ref
62
+ parser before name disambiguation returned 55 edges. With module blocks
63
+ and brace-matched bodies, vpc `b3abd6df` is 119 edges / 96 nodes
64
+ (root `*.tf` files alone: 93 edges / 73 nodes). `pyproject.toml` and
65
+ `requirements.txt` are parsers. A `]` inside a requirement extra does
66
+ not end a multiline TOML array.
67
+
5
68
  ## 0.7.4 — Terraform count/for_each + Kubernetes auto-detect fixes
6
69
 
7
70
  - **`extract.from_terraform`** now resolves `count` / `for_each`-indexed
8
71
  references (`aws_subnet.this[0].id`, `aws_eip.nat[count.index].id`) — the
9
72
  form real modules use almost universally. The ref regex previously matched
10
- only `type.name.` and missed them. On terraform-aws-modules/terraform-aws-vpc
11
- this goes from 2 to 56 edges.
73
+ only `type.name.` and missed them. The note below this release said
74
+ that went from 2 to 56 edges; a remeasure of vpc `b3abd6df` is under
75
+ Unreleased (119 edges / 96 nodes with the current parser).
12
76
  - **`extract.from_directory`** now detects Kubernetes manifests by content
13
77
  (`apiVersion` + `kind`) in any directory, not just conventional names like
14
78
  `k8s/` / `manifests/`. Manifests under `release/` or `kubernetes-manifests/`
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: semanticembed
3
- Version: 0.7.4
3
+ Version: 0.7.5
4
4
  Summary: 6D structural intelligence for directed graphs. Six numbers per node. Sub-millisecond.
5
5
  Project-URL: Homepage, https://github.com/jmurray10/semanticembed-sdk
6
6
  Project-URL: Documentation, https://github.com/jmurray10/semanticembed-sdk
@@ -22,13 +22,13 @@ Classifier: Topic :: Software Development :: Libraries
22
22
  Requires-Python: >=3.9
23
23
  Requires-Dist: httpx>=0.24
24
24
  Provides-Extra: agent-claude
25
- Requires-Dist: claude-agent-sdk; extra == 'agent-claude'
25
+ Requires-Dist: claude-agent-sdk>=0.1.60; extra == 'agent-claude'
26
26
  Requires-Dist: pyyaml>=6.0; extra == 'agent-claude'
27
27
  Provides-Extra: agent-gemini
28
28
  Requires-Dist: google-genai; extra == 'agent-gemini'
29
29
  Requires-Dist: pyyaml>=6.0; extra == 'agent-gemini'
30
30
  Provides-Extra: all
31
- Requires-Dist: claude-agent-sdk; extra == 'all'
31
+ Requires-Dist: claude-agent-sdk>=0.1.60; extra == 'all'
32
32
  Requires-Dist: google-genai; extra == 'all'
33
33
  Requires-Dist: pyyaml>=6.0; extra == 'all'
34
34
  Provides-Extra: dev
@@ -63,7 +63,7 @@ SemanticEmbed computes a 6-dimensional structural encoding for every node in a d
63
63
  > - **[Hugging Face Space](https://huggingface.co/spaces/jmurray10/semanticembed-agent-risk)** — zero-install. Paste a LangGraph / CrewAI / AutoGen file, get the encoding + risk findings.
64
64
  > - **[Demo dashboard](https://semanticembed-dashboard.vercel.app/)** — interactive 6D explorer with 4 reference apps (sign-in required; click "Try Demo" on the login page for a no-account walkthrough).
65
65
 
66
- > **Validated against production incidents.** In a blind test against a live production Dynatrace environment (108 services, 569 topology-relevant incidents over 30 days), **79.6%** of incidents (453/569) occurred on nodes that 6D structural analysis had flagged as risky — from the call graph alone, before any incident occurred. See [validation methodology](docs/validation_methodology.md).
66
+ > **One private-environment blind test.** On one Dynatrace tenant (108 services, 569 topology-relevant incidents over 30 days), **79.6%** of those incidents (453/569) were on nodes the hosted scorer had flagged. That run is not reproducible from this repo: the matcher and the risk thresholds live in a private service. See [validation methodology](docs/validation_methodology.md). It is one tenant, not a general accuracy number.
67
67
 
68
68
  ---
69
69
 
@@ -72,10 +72,10 @@ SemanticEmbed computes a 6-dimensional structural encoding for every node in a d
72
72
  Observability tools tell you **what broke**. SemanticEmbed tells you **what will break** — from topology alone.
73
73
 
74
74
  - **No agents, no instrumentation** — just an edge list
75
- - **Sub-millisecond server-side compute** — the encoding kernel itself runs in <1 ms on a 100-node graph; full HTTP round-trip via the cloud API is typically 0.5–2 s including network
75
+ - **Sub-millisecond server-side compute is not something this SDK can show.** `result.encoding_time_ms` is the HTTP round trip, not a kernel timer. The encode response has no separate compute-time field. From this environment on 2026-11-04 a 7-node graph returned in about 0.3–0.4 s, which is faster than the old "typically 0.5–2 s" line. A 100-node kernel time is not measurable on the free tier (the client refuses graphs over 50 nodes before the request).
76
76
  - **Works on any directed graph** — AI agent pipelines, microservices, data workflows, CI/CD
77
77
  - **Complementary structural axes** — six dimensions, each captures risk signals the others cannot
78
- - **14 deterministic edge parsers + 3 live connectors** — go from real infra to encoded result in 2 lines
78
+ - **16 deterministic edge parsers + 3 live connectors** — docker-compose, Kubernetes, GitHub Actions, Terraform, CloudFormation, CDK, Pulumi, Python imports, package.json, npm workspaces, pyproject.toml, requirements.txt, OTel JSON, LangGraph, CrewAI, AutoGen; plus Dynatrace, Honeycomb, and Datadog clients
79
79
 
80
80
  ---
81
81
 
@@ -91,39 +91,63 @@ pip install 'semanticembed[agent-claude]' # adds Claude agent CLI
91
91
 
92
92
  ---
93
93
 
94
- ## Quick Start — from real infra to risk in 2 lines
94
+ ## Quick Start
95
+
96
+ `from_directory` discovers edges. `encode` sends the edge list to the hosted API. Risk text is produced on the server; this package only prints it.
97
+
98
+ The block below is the report the API returned on **2026-11-04** for this edge list. If the server scorer changes, the words change. It is not a fixture baked into the client.
95
99
 
96
100
  ```python
97
101
  import semanticembed as se
98
102
 
99
- # Auto-discover edges from any directory: docker-compose, k8s, terraform,
100
- # CloudFormation, AWS CDK, Pulumi, GitHub Actions, package.json,
101
- # pyproject.toml, OTel traces, Python imports, LangGraph, CrewAI, AutoGen.
102
- edges, sources = se.extract.from_directory(".")
103
- print(f"Found {len(edges)} edges from {sources}")
104
-
105
- # 6D encode + structural risk analysis (sub-millisecond on the server side).
103
+ edges = [
104
+ ("frontend", "api-gateway"),
105
+ ("api-gateway", "order-service"),
106
+ ("api-gateway", "user-service"),
107
+ ("order-service", "payment-service"),
108
+ ("order-service", "inventory-service"),
109
+ ("payment-service", "database"),
110
+ ]
106
111
  result = se.encode(edges)
107
- print(result.table)
108
112
  print(se.report(result))
109
113
  ```
110
114
 
111
- Output:
115
+ Output (hosted API, 2026-11-04):
112
116
 
113
117
  ```
114
118
  STRUCTURAL RISK REPORT
115
119
  ======================
116
120
 
117
- AMPLIFICATION RISKS (high fanout, high criticality):
118
- - api-gateway | fanout=0.667 | criticality=0.556
121
+ SINGLE POINT OF FAILURE:
122
+ - order-service | order-service has high structural criticality (0.122) with limited lateral redundancy (independence=0.00). Failure here impacts 6 transitive path pairs.
123
+ - api-gateway | api-gateway has high structural criticality (0.102) with limited lateral redundancy (independence=0.50). Failure here impacts 5 transitive path pairs.
119
124
 
120
- CONVERGENCE SINKS (low independence, many upstream callers):
121
- - database | independence=0.000
125
+ MONITORING GAP CANDIDATE:
126
+ - order-service | order-service has high criticality (0.122) but sits at depth 0.50, not at the service mesh edge. Likely missing from edge-level observability.
127
+ - api-gateway | api-gateway has high criticality (0.102) but sits at depth 0.25, not at the service mesh edge. Likely missing from edge-level observability.
128
+ - payment-service | payment-service has high criticality (0.061) but sits at depth 0.75, not at the service mesh edge. Likely missing from edge-level observability.
122
129
 
123
- STRUCTURAL SPOF (low independence, high upstream dependency):
124
- - api-gateway | independence=0.000 | every request flows through this node
130
+ DEEP BOTTLENECK:
131
+ - order-service | order-service is a bottleneck deep in the pipeline (depth=0.50, criticality=0.122). Failures here are harder to diagnose from edge monitoring.
132
+ - payment-service | payment-service is a bottleneck deep in the pipeline (depth=0.75, criticality=0.061). Failures here are harder to diagnose from edge monitoring.
133
+
134
+ WHAT TO DO
135
+ ----------
136
+ >> api-gateway, order-service: Add a redundant path or fallback service so this node is not the only option at its pipeline stage.
137
+ >> api-gateway, order-service, payment-service: Ensure this node has dedicated monitoring. It sits on critical paths but is not at the service edge, so it may be missing from your dashboards.
138
+ >> order-service, payment-service: Add health checks and timeouts. Deep bottlenecks are hard to diagnose from edge monitoring — ensure this node has dedicated alerting.
139
+
140
+ Total: 7 risks (5 critical, 2 warning, 0 info)
125
141
  ```
126
142
 
143
+ Directory scan is the same `encode` call after `edges, sources = se.extract.from_directory(".")`. The 16 parsers are docker-compose, Kubernetes, GitHub Actions, Terraform, CloudFormation, AWS CDK (Python), Pulumi (Python), Python imports, `package.json`, npm workspaces, `pyproject.toml`, `requirements.txt`, OpenTelemetry JSON, LangGraph, CrewAI, and AutoGen. The three live connectors are Dynatrace, Honeycomb, and Datadog; this check did not call those APIs.
144
+
145
+ **Held-out extraction, 2026-11-04.** Hand-labeled files. Micro precision **1.0**, recall **1.0** (55/55 edges, 6 files). Tuning was also 1.0 (59/59 edges, 13 files). Pinned SHAs are in [corpus/manifest.json](corpus/manifest.json). Re-run with `python scripts/corpus_score.py --split held-out`. The only pin that was behind HEAD was this repo (`a612cec` vs `4f1cec9`); the labeled files in it produced the same edges. `package.json` workspaces are covered by a unit test, not by a pinned monorepo.
146
+
147
+ Two held-out files sit in repos that also contain a tuning file (`crewAI-examples` recruitment crew, `pulumi/examples` S3 website). Those files were not used to edit the parsers.
148
+
149
+ What to expect from hosted scoring on the voting-app shape, identical twins, and cyclic LangGraph graphs is in [docs/scoring_gaps.md](docs/scoring_gaps.md). The client forwards the edge list unchanged; those outcomes come from the service, not from client bugs.
150
+
127
151
  **Or try it without installing** — [open the Quickstart in Google Colab](https://colab.research.google.com/github/jmurray10/semanticembed-sdk/blob/main/notebooks/01_quickstart.ipynb).
128
152
 
129
153
  ---
@@ -228,6 +252,10 @@ edges = se.extract.from_github_actions(".github/workflows")
228
252
 
229
253
  # From Terraform
230
254
  edges = se.extract.from_terraform("infra/")
255
+ # Resource names that collide (`aws_vpc.this` and `aws_internet_gateway.this`)
256
+ # are labeled `type.name`. On terraform-aws-modules/terraform-aws-vpc
257
+ # `b3abd6df` that is 119 edges and 96 nodes, over the free-tier cap.
258
+ # The root `*.tf` files alone are 73 nodes. A license is required to encode them.
231
259
 
232
260
  # From CloudFormation (YAML or JSON)
233
261
  edges = se.extract.from_cloudformation("template.yaml")
@@ -415,8 +443,10 @@ The agent has 7 tools: scan, extract (docker-compose, k8s, Python imports), enco
415
443
 
416
444
  Be explicit about data egress before pointing the agent at private architecture:
417
445
 
418
- - **Claude agent** (`semanticembed-agent` / `python -m semanticembed.agent`): the LLM reads tool outputs as conversation context, so the contents of `docker-compose.yml`, Kubernetes manifests, Terraform `.tf` files, Python source, and `package.json` files in your project go to **Anthropic's API** along with your prompts. Conversation history is governed by Anthropic's data-use policies.
446
+ - **Claude agent** (`semanticembed-agent` / `python -m semanticembed.agent`): the LLM sees your prompts and what the SemanticEmbed tools return: edge lists (node names), which parsers found them, encoding results, and error messages. That goes to **Anthropic's API** and is governed by Anthropic's data-use policies. The agent has no built-in tools (no shell, file read, or web fetch), and it does not load `.claude/settings.json`, hooks, or `CLAUDE.md` from the project it scans. Requires `claude-agent-sdk>=0.1.60`.
419
447
  - **Gemini agent** (`semanticembed-gemini-agent` / `python -m semanticembed.agent.gemini_agent`): same data flow, sent to **Google's API** instead.
448
+ - **`find_edges()` LLM fallback**: only when the deterministic scan finds no edges, it sends the text of compose, Kubernetes, Terraform, GitHub Actions, `package.json`, `pyproject.toml`, `Pipfile` and `requirements.txt` files (up to 250 KB) to **Anthropic** (`provider="claude"`) or **Google** (`provider="gemini"`). It skips `node_modules`, `.venv`, `vendor` and similar directories, skips files that resolve outside the scanned path, and replaces secret-looking values (keys, tokens, passwords, URL credentials, Kubernetes `Secret` data) with `[REDACTED]`. Redaction is pattern-based and can miss a secret.
449
+ - **`explain()` / `ask()`**: send the encoding result (node names, the six values per node, risk flags, graph size) and your question to the model you name: **OpenAI**, **Anthropic**, or a local **Ollama** server. No file contents.
420
450
  - **Claude Code skill** (`skill/analyze.py`): runs **inside Claude Code** — uses the parent agent for any natural-language extraction, the SDK for the deterministic scan + encoding. No second LLM, no Ollama dependency.
421
451
  - **Cloud `encode()` call**: only the **edge list** (node names, e.g. `["frontend", "auth"]`) goes to the SemanticEmbed Railway endpoint. File contents are never sent.
422
452