forge-agent-bridge 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. forge_agent_bridge-0.2.0/.github/workflows/ci.yml +45 -0
  2. forge_agent_bridge-0.2.0/.github/workflows/provider-homologation.yml +39 -0
  3. forge_agent_bridge-0.2.0/.github/workflows/publish-pypi.yml +218 -0
  4. forge_agent_bridge-0.2.0/.gitignore +8 -0
  5. forge_agent_bridge-0.2.0/PKG-INFO +10 -0
  6. forge_agent_bridge-0.2.0/README.md +238 -0
  7. forge_agent_bridge-0.2.0/docs/API.md +238 -0
  8. forge_agent_bridge-0.2.0/docs/CAPABILITY_MATRIX.md +50 -0
  9. forge_agent_bridge-0.2.0/docs/DEVIN_INTEGRATION.md +18 -0
  10. forge_agent_bridge-0.2.0/docs/HOMOLOGATION.md +65 -0
  11. forge_agent_bridge-0.2.0/docs/PROGRAM_STATUS.md +19 -0
  12. forge_agent_bridge-0.2.0/docs/PROVIDERS.md +82 -0
  13. forge_agent_bridge-0.2.0/docs/SECURITY.md +85 -0
  14. forge_agent_bridge-0.2.0/docs/THREAT_MODEL.md +58 -0
  15. forge_agent_bridge-0.2.0/docs/adr/ADR-001-repo-and-architecture.md +25 -0
  16. forge_agent_bridge-0.2.0/docs/audits/CROSS_REPO_COMPATIBILITY.md +34 -0
  17. forge_agent_bridge-0.2.0/docs/audits/CYCLE30_1_BASELINE.md +44 -0
  18. forge_agent_bridge-0.2.0/docs/audits/CYCLE30_1_FINAL_HOMOLOGATION.md +106 -0
  19. forge_agent_bridge-0.2.0/docs/audits/CYCLE30_1_LIVE_HOMOLOGATION_20261030.md +90 -0
  20. forge_agent_bridge-0.2.0/docs/audits/CYCLE30_2_BASELINE.md +53 -0
  21. forge_agent_bridge-0.2.0/docs/audits/CYCLE30_2_FINAL_HOMOLOGATION.md +125 -0
  22. forge_agent_bridge-0.2.0/docs/audits/PROVIDER_GAP_MATRIX.md +36 -0
  23. forge_agent_bridge-0.2.0/docs/audits/TRUST_BOUNDARY_MATRIX.md +33 -0
  24. forge_agent_bridge-0.2.0/docs/audits/evidence/bedrock_20261030T094324.log +10 -0
  25. forge_agent_bridge-0.2.0/docs/audits/evidence/copilot_20261030T093705.jsonl +29 -0
  26. forge_agent_bridge-0.2.0/docs/audits/evidence/copilot_session_47153602/.workspace-fork.lock +0 -0
  27. forge_agent_bridge-0.2.0/docs/audits/evidence/copilot_session_47153602/checkpoints/index.md +6 -0
  28. forge_agent_bridge-0.2.0/docs/audits/evidence/copilot_session_47153602/events.jsonl +20 -0
  29. forge_agent_bridge-0.2.0/docs/audits/evidence/copilot_session_47153602/rewind-file-snapshots/tracking.json +1 -0
  30. forge_agent_bridge-0.2.0/docs/audits/evidence/copilot_session_47153602/workspace.yaml +9 -0
  31. forge_agent_bridge-0.2.0/docs/audits/evidence/devin_acp_20261030T093504.jsonl +51 -0
  32. forge_agent_bridge-0.2.0/docs/audits/evidence/e2e_cycle30_1/e2e_log.jsonl +49 -0
  33. forge_agent_bridge-0.2.0/docs/audits/evidence/e2e_cycle30_1/fab.log +0 -0
  34. forge_agent_bridge-0.2.0/docs/audits/evidence/e2e_cycle30_1/pf_api.log +0 -0
  35. forge_agent_bridge-0.2.0/docs/audits/evidence/e2e_cycle30_1/shots/01_chat_sessions.png +0 -0
  36. forge_agent_bridge-0.2.0/docs/audits/evidence/e2e_cycle30_1/shots/02_sessions.png +0 -0
  37. forge_agent_bridge-0.2.0/docs/audits/evidence/pf_verify/fab.log +0 -0
  38. forge_agent_bridge-0.2.0/docs/audits/evidence/pf_verify/pf.log +0 -0
  39. forge_agent_bridge-0.2.0/docs/audits/evidence/pf_verify/result.json +13 -0
  40. forge_agent_bridge-0.2.0/docs/codebase/.codebase-scan.txt +267 -0
  41. forge_agent_bridge-0.2.0/docs/codebase/ARCHITECTURE.md +52 -0
  42. forge_agent_bridge-0.2.0/docs/codebase/CONCERNS.md +52 -0
  43. forge_agent_bridge-0.2.0/docs/codebase/CONVENTIONS.md +41 -0
  44. forge_agent_bridge-0.2.0/docs/codebase/INTEGRATIONS.md +44 -0
  45. forge_agent_bridge-0.2.0/docs/codebase/STACK.md +51 -0
  46. forge_agent_bridge-0.2.0/docs/codebase/STRUCTURE.md +42 -0
  47. forge_agent_bridge-0.2.0/docs/codebase/TESTING.md +48 -0
  48. forge_agent_bridge-0.2.0/docs/providers/HOMOLOGATION_EVIDENCE.md +51 -0
  49. forge_agent_bridge-0.2.0/docs/providers/LIVE_CAPABILITY_MATRIX.md +67 -0
  50. forge_agent_bridge-0.2.0/docs/providers/evidence/codex/0.163.0/bridge.log +0 -0
  51. forge_agent_bridge-0.2.0/docs/providers/evidence/codex/0.163.0/resume.json +4 -0
  52. forge_agent_bridge-0.2.0/docs/providers/evidence/codex/0.163.0/turn1.json +33 -0
  53. forge_agent_bridge-0.2.0/e2e/test.txt +1 -0
  54. forge_agent_bridge-0.2.0/forge.json +17 -0
  55. forge_agent_bridge-0.2.0/install.ps1 +50 -0
  56. forge_agent_bridge-0.2.0/install.sh +68 -0
  57. forge_agent_bridge-0.2.0/install.windows.sh +156 -0
  58. forge_agent_bridge-0.2.0/pyproject.toml +34 -0
  59. forge_agent_bridge-0.2.0/scripts/compat_check.py +121 -0
  60. forge_agent_bridge-0.2.0/scripts/e2e_cycle30_1.py +500 -0
  61. forge_agent_bridge-0.2.0/scripts/forge_bootstrap.py +1291 -0
  62. forge_agent_bridge-0.2.0/scripts/provider_harness.py +145 -0
  63. forge_agent_bridge-0.2.0/scripts/release_gate.sh +35 -0
  64. forge_agent_bridge-0.2.0/src/forge_agent_bridge/__init__.py +11 -0
  65. forge_agent_bridge-0.2.0/src/forge_agent_bridge/acp.py +171 -0
  66. forge_agent_bridge-0.2.0/src/forge_agent_bridge/adapter.py +57 -0
  67. forge_agent_bridge-0.2.0/src/forge_agent_bridge/approvals.py +113 -0
  68. forge_agent_bridge-0.2.0/src/forge_agent_bridge/backup.py +67 -0
  69. forge_agent_bridge-0.2.0/src/forge_agent_bridge/broker.py +103 -0
  70. forge_agent_bridge-0.2.0/src/forge_agent_bridge/budgets.py +104 -0
  71. forge_agent_bridge-0.2.0/src/forge_agent_bridge/cli.py +191 -0
  72. forge_agent_bridge-0.2.0/src/forge_agent_bridge/collab.py +133 -0
  73. forge_agent_bridge-0.2.0/src/forge_agent_bridge/context.py +254 -0
  74. forge_agent_bridge-0.2.0/src/forge_agent_bridge/contracts.py +113 -0
  75. forge_agent_bridge-0.2.0/src/forge_agent_bridge/events.py +209 -0
  76. forge_agent_bridge-0.2.0/src/forge_agent_bridge/files.py +134 -0
  77. forge_agent_bridge-0.2.0/src/forge_agent_bridge/install/__init__.py +1 -0
  78. forge_agent_bridge-0.2.0/src/forge_agent_bridge/install/cli.py +99 -0
  79. forge_agent_bridge-0.2.0/src/forge_agent_bridge/install/lifecycle.py +254 -0
  80. forge_agent_bridge-0.2.0/src/forge_agent_bridge/integrity.py +112 -0
  81. forge_agent_bridge-0.2.0/src/forge_agent_bridge/intelligence.py +261 -0
  82. forge_agent_bridge-0.2.0/src/forge_agent_bridge/local_bridge.py +1430 -0
  83. forge_agent_bridge-0.2.0/src/forge_agent_bridge/metadata.py +72 -0
  84. forge_agent_bridge-0.2.0/src/forge_agent_bridge/orchestrator.py +126 -0
  85. forge_agent_bridge-0.2.0/src/forge_agent_bridge/pairing.py +144 -0
  86. forge_agent_bridge-0.2.0/src/forge_agent_bridge/pf_context.py +122 -0
  87. forge_agent_bridge-0.2.0/src/forge_agent_bridge/providers/__init__.py +0 -0
  88. forge_agent_bridge-0.2.0/src/forge_agent_bridge/providers/acp_adapter.py +233 -0
  89. forge_agent_bridge-0.2.0/src/forge_agent_bridge/providers/claude.py +292 -0
  90. forge_agent_bridge-0.2.0/src/forge_agent_bridge/providers/codex.py +223 -0
  91. forge_agent_bridge-0.2.0/src/forge_agent_bridge/providers/copilot.py +116 -0
  92. forge_agent_bridge-0.2.0/src/forge_agent_bridge/providers/fake.py +88 -0
  93. forge_agent_bridge-0.2.0/src/forge_agent_bridge/recovery.py +44 -0
  94. forge_agent_bridge-0.2.0/src/forge_agent_bridge/registry.py +189 -0
  95. forge_agent_bridge-0.2.0/src/forge_agent_bridge/runners.py +111 -0
  96. forge_agent_bridge-0.2.0/src/forge_agent_bridge/secrets.py +116 -0
  97. forge_agent_bridge-0.2.0/src/forge_agent_bridge/ssrf.py +108 -0
  98. forge_agent_bridge-0.2.0/src/forge_agent_bridge/ui/__init__.py +29 -0
  99. forge_agent_bridge-0.2.0/src/forge_agent_bridge/ui/i18n.py +143 -0
  100. forge_agent_bridge-0.2.0/src/forge_agent_bridge/ui/kit.py +689 -0
  101. forge_agent_bridge-0.2.0/src/forge_agent_bridge/ui/wizard.py +317 -0
  102. forge_agent_bridge-0.2.0/tests/conftest.py +40 -0
  103. forge_agent_bridge-0.2.0/tests/fake_acp_server.py +39 -0
  104. forge_agent_bridge-0.2.0/tests/test_bridge.py +268 -0
  105. forge_agent_bridge-0.2.0/tests/test_cycle21.py +149 -0
  106. forge_agent_bridge-0.2.0/tests/test_cycle22.py +123 -0
  107. forge_agent_bridge-0.2.0/tests/test_cycle23.py +96 -0
  108. forge_agent_bridge-0.2.0/tests/test_cycle24.py +160 -0
  109. forge_agent_bridge-0.2.0/tests/test_cycle25.py +163 -0
  110. forge_agent_bridge-0.2.0/tests/test_cycle26.py +117 -0
  111. forge_agent_bridge-0.2.0/tests/test_cycle27.py +115 -0
  112. forge_agent_bridge-0.2.0/tests/test_cycle28.py +117 -0
  113. forge_agent_bridge-0.2.0/tests/test_cycle29.py +86 -0
  114. forge_agent_bridge-0.2.0/tests/test_cycle30.py +116 -0
  115. forge_agent_bridge-0.2.0/tests/test_cycle30_1.py +130 -0
  116. forge_agent_bridge-0.2.0/tests/test_cycle30_2.py +265 -0
  117. forge_agent_bridge-0.2.0/tests/test_install.py +81 -0
  118. forge_agent_bridge-0.2.0/tests/test_perf_smoke.py +90 -0
  119. forge_agent_bridge-0.2.0/tests/test_provider_harness.py +19 -0
  120. forge_agent_bridge-0.2.0/uv.lock +362 -0
@@ -0,0 +1,45 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ core:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: "3.12"
16
+ - name: Install
17
+ run: pip install -e ".[dev]"
18
+ - name: Lint
19
+ run: python -m ruff check src tests
20
+ - name: Tests
21
+ run: python -m pytest -q
22
+
23
+ security:
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: actions/setup-python@v5
28
+ with:
29
+ python-version: "3.12"
30
+ - name: Install
31
+ run: pip install -e .
32
+ - name: Dependency audit
33
+ run: |
34
+ pip install pip-audit
35
+ pip-audit --desc on
36
+ - name: SBOM (CycloneDX)
37
+ run: |
38
+ pip install "cyclonedx-bom>=7,<8"
39
+ cyclonedx-py environment "$(command -v python3)" --of JSON -o sbom.json
40
+ python -c "import json; json.load(open('sbom.json'))"
41
+ ls -l sbom.json
42
+ - uses: actions/upload-artifact@v4
43
+ with:
44
+ name: sbom
45
+ path: sbom.json
@@ -0,0 +1,39 @@
1
+ name: Provider homologation
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ schedule:
6
+ - cron: "17 3 * * *"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ real-provider-matrix:
13
+ runs-on: ubuntu-latest
14
+ timeout-minutes: 20
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: astral-sh/setup-uv@v5
18
+ with:
19
+ enable-cache: true
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.12"
23
+ - run: uv sync --extra dev
24
+ - name: Run redacted real-provider matrix
25
+ env:
26
+ # Provider credentials are configured only in the repository/org
27
+ # secret store by the operator. Missing auth is reported as blocked.
28
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
29
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
30
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
31
+ AWS_SESSION_TOKEN: ${{ secrets.AWS_SESSION_TOKEN }}
32
+ AWS_REGION: ${{ secrets.AWS_REGION }}
33
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34
+ run: uv run python scripts/provider_harness.py --real --output artifacts/provider-matrix.json
35
+ - uses: actions/upload-artifact@v4
36
+ if: always()
37
+ with:
38
+ name: provider-homologation-${{ github.run_id }}
39
+ path: artifacts/provider-matrix.json
@@ -0,0 +1,218 @@
1
+ name: publish-pypi
2
+
3
+ # Secure PyPI release pipeline — build once, publish the same artifact.
4
+ #
5
+ # build — version gate + `python -m build` + integrity manifest + artifact
6
+ # upload (contents:read only)
7
+ # publish — downloads the SAME dist/ built above, re-verifies sha256, then
8
+ # publishes via PyPI Trusted Publishing (OIDC — no stored token)
9
+ # verify — installs the published version from PyPI in a clean venv and
10
+ # smoke-tests the console script
11
+ #
12
+ # Trusted Publishing (OIDC) — no stored token. The publisher registered on
13
+ # PyPI must point to:
14
+ # owner/repo: EdgarSocrates98/forge-agent-bridge
15
+ # workflow: publish-pypi.yml
16
+ # environment: release
17
+ #
18
+ # `environment: release` also enables repo protection — configure required
19
+ # reviewers/rules under Settings > Environments > release.
20
+ #
21
+ # GitHub artifact attestations (PEP 740) are intentionally disabled to keep
22
+ # this pipeline minimal; to enable later, add `attestations: write` to the
23
+ # publish job permissions and `attestations: true` to the pypi-publish inputs.
24
+ on:
25
+ workflow_dispatch:
26
+ inputs:
27
+ version:
28
+ description: "Version to publish — must match the package version (pyproject or hatch source)"
29
+ required: true
30
+ type: string
31
+
32
+ permissions:
33
+ contents: read
34
+
35
+ # one publish run at a time — never cancel mid-publish
36
+ concurrency:
37
+ group: publish-pypi
38
+ cancel-in-progress: false
39
+
40
+ jobs:
41
+ build:
42
+ name: Build release artifact
43
+ runs-on: ubuntu-latest
44
+ permissions:
45
+ contents: read
46
+ steps:
47
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
48
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
49
+ with:
50
+ python-version: "3.12"
51
+
52
+ - name: Version gate (input match, PyPI freshness, changelog)
53
+ env:
54
+ EXPECTED_VERSION: ${{ inputs.version }}
55
+ run: |
56
+ python - <<'PY'
57
+ import json, os, re, sys, tomllib, urllib.error, urllib.request
58
+
59
+ def fail(msg):
60
+ print(f"::error::version gate: {msg}")
61
+ sys.exit(1)
62
+
63
+ with open("pyproject.toml", "rb") as fh:
64
+ pyproject = tomllib.load(fh)
65
+ project = pyproject["project"]
66
+ name = project["name"]
67
+ version = project.get("version")
68
+ if version is None:
69
+ # dynamic version — resolve through [tool.hatch.version] path,
70
+ # the same source hatchling reads at build time.
71
+ path = pyproject.get("tool", {}).get("hatch", {}).get("version", {}).get("path")
72
+ if not path:
73
+ fail("version is dynamic but [tool.hatch.version].path is not declared")
74
+ src = open(path, encoding="utf-8").read()
75
+ match = re.search(r'__version__\s*=\s*"([^"]+)"', src)
76
+ if not match:
77
+ fail(f"could not resolve __version__ from {path}")
78
+ version = match.group(1)
79
+ print(f"package version: {name} == {version}")
80
+
81
+ expected = os.environ.get("EXPECTED_VERSION", "").strip()
82
+ if expected:
83
+ if version != expected:
84
+ fail(f"dispatch input {expected!r} != package version {version!r}")
85
+ print(f"dispatch input matches package version: {version}")
86
+ else:
87
+ print("no version input supplied — input-match check skipped")
88
+
89
+ pypi_name = re.sub(r"[-_.]+", "-", name).lower()
90
+ url = f"https://pypi.org/pypi/{pypi_name}/json"
91
+ releases = None
92
+ try:
93
+ with urllib.request.urlopen(url, timeout=15) as resp:
94
+ releases = json.load(resp).get("releases", {})
95
+ except urllib.error.HTTPError as e:
96
+ if e.code == 404:
97
+ print(f"{pypi_name}: not on PyPI yet (HTTP 404) — first publish")
98
+ else:
99
+ fail(f"PyPI JSON returned HTTP {e.code}; refusing to publish unverified")
100
+ except Exception as e:
101
+ fail(f"PyPI check failed: {e}")
102
+ if releases is not None and version in releases:
103
+ fail(f"{pypi_name} {version} is already published on PyPI")
104
+
105
+ if os.path.exists("CHANGELOG.md"):
106
+ lines = open("CHANGELOG.md", encoding="utf-8").read().splitlines()
107
+ tracks = any(re.match(r"##\s*\[?\d+\.\d+", l) for l in lines[:40])
108
+ if not tracks:
109
+ print("CHANGELOG.md does not track versions — changelog gate skipped")
110
+ elif any(l.startswith("##") and version in l for l in lines):
111
+ print(f"CHANGELOG.md has an entry for {version}")
112
+ else:
113
+ fail(f"CHANGELOG.md tracks versions but has no entry for {version}")
114
+ else:
115
+ print("no CHANGELOG.md — changelog gate skipped")
116
+
117
+ print(f"version gate OK: {name} {version}")
118
+ PY
119
+
120
+ - name: Build wheel + sdist
121
+ run: python -m pip install --quiet build twine && python -m build
122
+
123
+ - name: Twine check
124
+ run: python -m twine check dist/*
125
+
126
+ - name: Inspect wheel contents
127
+ run: |
128
+ for whl in dist/*.whl; do
129
+ echo "== $whl"
130
+ python -m zipfile -l "$whl"
131
+ done
132
+
133
+ - name: Record artifact checksums
134
+ run: (cd dist && sha256sum -- *) | tee dist-checksums.txt
135
+
136
+ - name: Upload release artifact
137
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
138
+ with:
139
+ name: release-dist
140
+ path: |
141
+ dist/
142
+ dist-checksums.txt
143
+ if-no-files-found: error
144
+ retention-days: 7
145
+
146
+ publish:
147
+ name: Publish to PyPI
148
+ needs: build
149
+ runs-on: ubuntu-latest
150
+ # configure protection rules/reviewers for the "release" environment
151
+ # in repo Settings > Environments
152
+ environment: release
153
+ permissions:
154
+ contents: read
155
+ id-token: write # OIDC for PyPI trusted publishing — scoped to this job only
156
+ steps:
157
+ - name: Download release artifact
158
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
159
+ with:
160
+ name: release-dist
161
+
162
+ - name: Verify artifact integrity (same artifact that was built)
163
+ run: |
164
+ test -f dist-checksums.txt
165
+ cd dist && sha256sum --check ../dist-checksums.txt
166
+
167
+ - name: Publish to PyPI (trusted publishing)
168
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
169
+
170
+ verify:
171
+ name: Verify published package
172
+ needs: publish
173
+ runs-on: ubuntu-latest
174
+ permissions:
175
+ contents: read
176
+ steps:
177
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
178
+ with:
179
+ python-version: "3.12"
180
+
181
+ - name: Wait for release to appear on PyPI
182
+ env:
183
+ PKG_NAME: forge-agent-bridge
184
+ EXPECTED_VERSION: ${{ inputs.version }}
185
+ run: |
186
+ python - <<'PY'
187
+ import os, sys, time, urllib.request
188
+ name = os.environ["PKG_NAME"]
189
+ version = os.environ["EXPECTED_VERSION"]
190
+ url = f"https://pypi.org/pypi/{name}/{version}/json"
191
+ for attempt in range(1, 21): # up to ~5 min
192
+ try:
193
+ with urllib.request.urlopen(url, timeout=15) as resp:
194
+ if resp.status == 200:
195
+ print(f"{name} {version} live on PyPI (attempt {attempt})")
196
+ sys.exit(0)
197
+ except Exception as e:
198
+ print(f"attempt {attempt}/20: not live yet ({e})")
199
+ time.sleep(15)
200
+ sys.exit(f"{name} {version} not visible on PyPI after ~5 minutes")
201
+ PY
202
+
203
+ - name: Install published package in clean venv
204
+ run: |
205
+ python -m venv /tmp/verify-venv
206
+ for i in 1 2 3 4 5 6 7 8 9 10; do
207
+ if /tmp/verify-venv/bin/python -m pip install --no-cache-dir \
208
+ --index-url https://pypi.org/simple \
209
+ "forge-agent-bridge==${{ inputs.version }}"; then break; fi
210
+
211
+ echo "install attempt $i failed (index propagation); retrying in 30s"
212
+
213
+ sleep 30
214
+
215
+ done
216
+
217
+ - name: Smoke test console script
218
+ run: /tmp/verify-venv/bin/forge-bridge --version
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ .venv/
3
+ dist/
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ graphify-out/
7
+ .claude/
8
+ .claude-flow/
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.5
2
+ Name: forge-agent-bridge
3
+ Version: 0.2.0
4
+ Summary: Forge Agent Bridge — universal agent session/event/approval contracts and provider adapters (Claude Code, Codex, Devin)
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: pydantic>=2.6
7
+ Requires-Dist: typer>=0.12
8
+ Provides-Extra: dev
9
+ Requires-Dist: pytest>=8; extra == 'dev'
10
+ Requires-Dist: ruff>=0.6; extra == 'dev'
@@ -0,0 +1,238 @@
1
+ # Forge Agent Bridge
2
+
3
+ A **provider-agnostic agent session bridge** for the Forge ecosystem. One
4
+ local HTTP surface exposes Claude Code, Codex CLI and Devin sessions through a
5
+ single typed contract — so Forge Portal's Chat Studio (or any host) can create,
6
+ prompt, approve, replay and hand off agent sessions without knowing provider
7
+ CLI details.
8
+
9
+ Part of the [Product Forge](https://github.com/EdgarSocrates98/product-forge)
10
+ ecosystem. Architectural decision: [docs/adr/ADR-001-repo-and-architecture.md](docs/adr/ADR-001-repo-and-architecture.md).
11
+
12
+ ## What it does
13
+
14
+ | Capability | How |
15
+ |---|---|
16
+ | **Session orchestration** | `BridgeOrchestrator` — create → prompt → drain → replay → cancel → close per provider adapter |
17
+ | **Real provider adapters** | Claude (`stream-json` bidirectional), Codex (`exec --json` JSONL + resume), Devin (`acp` native), `fake` for tests |
18
+ | **Local bridge service** | stdlib HTTP on `127.0.0.1` only, pairing bearer token (`0600`), process supervisor |
19
+ | **Runner registry** | local + remote runners; remote starts `unverified`, health probe gates placement |
20
+ | **Budgets** | scoped limits (user/workspace/product/provider/session), day/month windows, real token recording |
21
+ | **Collaboration** | owner/observer/delegated-approver roles, visibility, comments — observers are read-only |
22
+ | **Audit** | append-only trail for session/runner/share/budget/approval/handoff actions |
23
+ | **Handoff** | `remote` / `provider-resume` / `context-rebuild` — never claims unverifiable continuity |
24
+ | **File service** | path-scoped, traversal-safe list/preview/download/upload; writes are approval-gated with versioned snapshots + restore |
25
+ | **Artifacts** | per-session artifact listing via adapters |
26
+ | **MCP config** | `mcp_servers` on session create, passed through to providers |
27
+
28
+ ## Honest capability states
29
+
30
+ Nothing is simulated. Every capability is a **named state**:
31
+
32
+ - `ready` — verified working (binary detected + mechanism confirmed)
33
+ - `unverified` — mechanism implemented, not yet exercised end-to-end
34
+ - `unavailable:<reason>` — binary absent, auth missing, transport unsupported
35
+ - `error` — last operation failed
36
+
37
+ Handoff distinguishes **`provider-resume`** (verified native resume: Codex,
38
+ Devin) from **`context-rebuild`** (reconstructed context — returns
39
+ `continuity: "reconstructed"`, never "identical"). Remote runner placement is
40
+ **refused** until a health probe verifies the runner.
41
+
42
+ See [docs/CAPABILITY_MATRIX.md](docs/CAPABILITY_MATRIX.md) for the live matrix
43
+ and [docs/HOMOLOGATION.md](docs/HOMOLOGATION.md) for release evidence.
44
+
45
+ ## Install
46
+
47
+ Two routes — same contract as `productforge install` / `sparkforge-aws install`:
48
+
49
+ ### Bootstrap scripts (user install, no checkout dependence)
50
+
51
+ | File | Route |
52
+ |---|---|
53
+ | `install.sh` | POSIX (Linux/macOS) |
54
+ | `install.windows.sh` | Windows Git Bash |
55
+ | `install.ps1` | Windows PowerShell |
56
+ | `scripts/forge_bootstrap.py` | shared engine, driven by `forge.json` |
57
+
58
+ ```bash
59
+ ./install.sh # or: install.ps1 / install.windows.sh
60
+ ./install.sh --dry-run # plan only
61
+ ./install.sh --editable # dev mode — CLI tied to this checkout
62
+ ```
63
+
64
+ The bootstrap detects Python → builds a wheel → creates a venv at
65
+ `~/.forge/installs/forge-agent-bridge` → writes the `forge-bridge` launcher
66
+ (`~/.local/bin` / `%USERPROFILE%\.local\bin`) → runs a `forge-bridge --version`
67
+ smoke → registers the install under `~/.forge/installations/`.
68
+
69
+ ### `forge-bridge install` (guided wizard / flag path)
70
+
71
+ Bare on a terminal, `forge-bridge install` runs the guided wizard:
72
+ **scope** (`user`·`project`·`workspace`) → **profile**
73
+ (`minimal`·`recommended`·`full`) → **components**
74
+ (`service`·`state`·`pairing`·`providers`·`config`) → real dry-run plan →
75
+ confirm → apply → doctor.
76
+
77
+ Non-interactive for CI:
78
+
79
+ ```bash
80
+ forge-bridge install --yes --scope user --profile recommended
81
+ forge-bridge install --dry-run # plan JSON, nothing written
82
+ forge-bridge install-status # per-check verification
83
+ forge-bridge uninstall --yes # removes generated artifacts —
84
+ # NEVER pair.token/events/backups
85
+ ```
86
+
87
+ Bridge-honest components (a localhost service ships no host payloads):
88
+
89
+ | Component | Effect |
90
+ |---|---|
91
+ | `service` | verifies `forge-bridge` on PATH (`unverified` until bootstrap ran) |
92
+ | `state` | `<root>` + `events/` + `backups/` dirs |
93
+ | `pairing` | `<root>/pair.token` 0600 (never rotated silently) |
94
+ | `providers` | `<root>/providers.json` — capability-matrix snapshot |
95
+ | `config` | `<root>/config.json` — seeded limits (rate/quota/retention) |
96
+
97
+ Default state root by scope: `user` → `~/.forge-bridge`,
98
+ `project` → `./.forge-bridge`, `workspace` → `<parent>/.forge-bridge`.
99
+
100
+ ### Dev
101
+
102
+ ```bash
103
+ python3 -m pip install -e ".[dev]"
104
+ forge-bridge --help
105
+ ```
106
+
107
+ No dependencies beyond Python 3.11+ (`typer`, `pydantic`).
108
+
109
+ ## Quickstart
110
+
111
+ ```bash
112
+ # 1. Pair — creates ~/.forge-bridge/pair.token (0600)
113
+ forge-bridge pair
114
+
115
+ # 2. Diagnostics — providers detected, auth states
116
+ forge-bridge doctor
117
+ forge-bridge providers
118
+
119
+ # 3. Serve the local bridge (127.0.0.1 only)
120
+ forge-bridge serve --port 8787
121
+
122
+ # 4. From any client — bearer auth, agent-session surface
123
+ TOKEN=$(cat ~/.forge-bridge/pair.token)
124
+ curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8787/status
125
+ curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
126
+ -d '{"provider":"claude","product_id":"payments","title":"retry analysis"}' \
127
+ http://127.0.0.1:8787/agent-sessions
128
+ curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
129
+ -d '{"text":"summarize the retry policy"}' \
130
+ http://127.0.0.1:8787/agent-sessions/{sid}/prompt
131
+ curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
132
+ -d '{"after_seq":0}' http://127.0.0.1:8787/agent-sessions/{sid}/events
133
+ ```
134
+
135
+ ## Provider adapters
136
+
137
+ | Provider | Mechanism | Resume | Status |
138
+ |---|---|---|---|
139
+ | **Claude Code** | `claude -p --output-format stream-json --input-format stream-json` | context-rebuild | binary detected |
140
+ | **Codex CLI** | `codex exec --json` (JSONL); `codex exec resume` | provider-resume | binary detected |
141
+ | **Devin** | `devin acp` (ACP stdio JSON-RPC) | provider-resume | binary detected |
142
+ | **fake** | in-process scripted events | — | test provider |
143
+
144
+ All adapters normalize provider output into `forge/AgentBridgeEvent/v1`
145
+ envelopes (seq, dedupe, replay buffer, unknown natives preserved in `raw`).
146
+
147
+ Details: [docs/PROVIDERS.md](docs/PROVIDERS.md).
148
+
149
+ ## HTTP surface
150
+
151
+ Auth: `Authorization: Bearer <pair.token>` on every request.
152
+ Identity: `X-FAB-Principal` (default `local`) — self-asserted; the pair token
153
+ is the trust boundary.
154
+
155
+ | Method | Path | Purpose |
156
+ |---|---|---|
157
+ | GET | `/status` | health, workspace root, running sessions |
158
+ | GET | `/providers` | capability matrix (detected + auth states) |
159
+ | GET | `/audit` | audit trail tail (`X-Tail` header) |
160
+ | POST | `/sessions` | raw process spawn (`cmd` string list) |
161
+ | DELETE | `/sessions/{id}` | terminate spawned process |
162
+ | POST | `/agent-sessions` | create agent session (provider, product, title, mcp_servers, runner) |
163
+ | GET | `/agent-sessions` | list live + closed sessions with metadata |
164
+ | PATCH | `/agent-sessions/{sid}` | update metadata (title, favorite, archived, visibility, notes) |
165
+ | DELETE | `/agent-sessions/{sid}` | close + archive session |
166
+ | POST | `/agent-sessions/{sid}/prompt` | send prompt (text, attachments) — authz + budget gated |
167
+ | POST | `/agent-sessions/{sid}/events` | replay events `after_seq` |
168
+ | POST | `/agent-sessions/{sid}/cancel` | cancel in-flight turn |
169
+ | POST | `/agent-sessions/{sid}/resume` | resume via provider `session_ref` |
170
+ | POST | `/agent-sessions/{sid}/approve` | resolve approval (owner/delegated) |
171
+ | POST | `/agent-sessions/{sid}/share` | set visibility/observers/delegation |
172
+ | POST | `/agent-sessions/{sid}/comment` | append comment |
173
+ | POST | `/agent-sessions/{sid}/handoff` | hand off (remote / provider-resume / context-rebuild) |
174
+ | POST | `/agent-sessions/{sid}/artifacts` | list session artifacts |
175
+ | POST | `/files/{op}` | `list` `preview` `download` `upload` `history` `restore` |
176
+ | GET | `/runners` | list runners |
177
+ | POST | `/runners` | register remote runner (`unverified`) |
178
+ | POST | `/runners/{id}/health` | probe health → `ready`/`error` |
179
+ | POST | `/runners/{id}/deregister` | remove remote runner (local refuses) |
180
+ | GET | `/budget` | usage summary per scope |
181
+ | POST | `/budget/limits` | set limit (scope, key, window, tokens) |
182
+
183
+ Errors are typed `FAB-*` codes in `{error: {code, message}}` envelopes;
184
+ `FORBIDDEN` → 403, `BUDGET` → 429, `NOT-FOUND` → 404, validation → 422,
185
+ malformed JSON → 422, adapter errors → 502.
186
+
187
+ Full reference: [docs/API.md](docs/API.md).
188
+
189
+ ## Security model
190
+
191
+ - **Localhost only** — binds `127.0.0.1`, never a public interface.
192
+ - **Pairing token** — generated `0600`, required on every request.
193
+ - **File safety** — root-scoped resolution, `..` traversal rejected, preview
194
+ size cap, mutation ops require `approved: true`, versioned snapshots before
195
+ overwrite, restore support.
196
+ - **Authorization** — owner-only actions on private sessions; observers
197
+ read-only; delegated approver can resolve approvals.
198
+ - **Budgets** — checked *before* prompt execution; usage recorded from real
199
+ provider `usage.updated` events where available (else `estimated: true`).
200
+ - **No secrets** — credentials never reach provider envs or logs.
201
+
202
+ Details: [docs/SECURITY.md](docs/SECURITY.md).
203
+
204
+ ## Layout
205
+
206
+ ```
207
+ src/forge_agent_bridge/
208
+ contracts.py # 20+ typed entities, CapabilityState
209
+ adapter.py # ProviderAdapter protocol + FAB-* errors
210
+ events.py # forge/AgentBridgeEvent/v1 buffer + replay
211
+ registry.py # declarative provider catalog + detection
212
+ orchestrator.py # adapter + normalize + buffer in one API
213
+ local_bridge.py # 127.0.0.1 HTTP surface (auth, routes, error mapping)
214
+ runners.py # runner registry + health lifecycle
215
+ budgets.py # scoped limits + usage ledger
216
+ collab.py # owner/observer/delegated authz + comments
217
+ metadata.py # persisted session metadata
218
+ files.py # path-scoped file ops + snapshots
219
+ acp.py # ACP stdio JSON-RPC client (Devin)
220
+ cli.py # forge-bridge status|doctor|providers|sessions|pair|serve
221
+ providers/ # claude.py, codex.py, acp_adapter.py, fake.py
222
+ tests/ # test_cycle19 … test_cycle24 (35 tests)
223
+ docs/ # CAPABILITY_MATRIX, HOMOLOGATION, ADR-001, API, PROVIDERS, SECURITY
224
+ ```
225
+
226
+ ## Development
227
+
228
+ ```bash
229
+ python -m pytest -q # 35 tests
230
+ python -m ruff check src tests # lint
231
+ forge-bridge doctor # environment diagnostics
232
+ ```
233
+
234
+ ## Consumer
235
+
236
+ **Forge Portal → Chat Studio** is the primary client: three-column session
237
+ workspace (sessions list, stream, context/files) driving every endpoint above
238
+ through the `PfApi` bridge service.