Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8041ced
feat: prompt users to reuse existing sessions before creating new one…
datasciencemonkey Apr 14, 2026
7e591fb
fix: add xterm.js ClipboardAddon for OSC 52 clipboard support
datasciencemonkey Apr 14, 2026
6286d0d
chore: pin mlflow-tracing to 3.10.1 in pyproject.toml
datasciencemonkey Apr 14, 2026
7438f56
chore: remove mlflow-tracing and opentelemetry dependencies
datasciencemonkey Apr 14, 2026
80c9e34
chore(deps): replace mlflow-tracing with mlflow-skinny 3.10.1
datasciencemonkey Apr 14, 2026
3dc828e
fix: batch terminal output writes to prevent escape sequence fragment…
datasciencemonkey Apr 14, 2026
1b47ef0
fix: exit alternate screen buffer on session reattach (#122)
datasciencemonkey Apr 14, 2026
7f05062
fix: clear screen after alternate screen buffer exit (#122)
datasciencemonkey Apr 14, 2026
8b5cd13
fix: clear screen after buffer replay on reattach (#122)
datasciencemonkey Apr 14, 2026
1efaa57
fix: use term.reset() + resize instead of buffer replay on reattach (…
datasciencemonkey Apr 14, 2026
e2807c2
fix: force SIGWINCH on reattach by toggling terminal size (#122)
datasciencemonkey Apr 14, 2026
fab0cf4
fix: address code review — escape key in prompt, batcher cleanup
datasciencemonkey Apr 14, 2026
846ae02
fix: address code review — TOCTOU race, test accuracy
datasciencemonkey Apr 14, 2026
e35ebaa
fix: show error message when session limit is reached
datasciencemonkey Apr 14, 2026
e656490
fix: correct repo name in deployment docs and README
datasciencemonkey Apr 14, 2026
098a9c4
fix: session count badge clipped by button overflow
datasciencemonkey Apr 14, 2026
99bf99a
fix: replace badge with text label in tab bar for session count
datasciencemonkey Apr 14, 2026
b4ac7b0
fix: update session count on close/exit, not just create
datasciencemonkey Apr 14, 2026
f828901
fix: close remaining gaps in session count updates
datasciencemonkey Apr 14, 2026
08e6dac
fix: retry session count update after backend cleanup delay
datasciencemonkey Apr 14, 2026
bc7ee10
chore: bump version to 0.17.0
datasciencemonkey Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ Production uses `workers=1` (PTY state is process-local), `threads=16` (concurre
<summary><strong>📁 Project Structure</strong></summary>

```
coding-agents-in-databricks/
coding-agents-databricks-apps/
├── app.py # Flask backend + PTY management + setup orchestration
├── app_state.py # Shared app state (setup progress, session registry)
├── app.yaml.template # Databricks Apps deployment config template
Expand Down
17 changes: 16 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
SESSION_TIMEOUT_SECONDS = 86400 # No poll for 24 hours = dead session
CLEANUP_INTERVAL_SECONDS = 900 # Check for stale sessions every 15 min
GRACEFUL_SHUTDOWN_WAIT = 3 # Seconds to wait after SIGHUP before SIGKILL
MAX_CONCURRENT_SESSIONS = int(os.environ.get("MAX_CONCURRENT_SESSIONS", "5"))

# Logging setup
logging.basicConfig(level=logging.INFO)
Expand Down Expand Up @@ -604,7 +605,7 @@ def read_pty_output(session_id, fd):
try:
readable, _, errors = select.select([fd], [], [fd], 0.05)
if readable or errors:
output = os.read(fd, 4096)
output = os.read(fd, 65536)
if not output:
# EOF — process exited
break
Expand Down Expand Up @@ -956,6 +957,11 @@ def configure_pat():
@app.route("/api/session", methods=["POST"])
def create_session():
"""Create a new terminal session."""
# Quick reject before forking a PTY (approximate — authoritative check below)
with sessions_lock:
if len(sessions) >= MAX_CONCURRENT_SESSIONS:
return jsonify({"error": f"Maximum {MAX_CONCURRENT_SESSIONS} concurrent sessions reached. Close an existing session first."}), 429

data = request.get_json(silent=True) or {}
label = data.get("label", "")
try:
Expand Down Expand Up @@ -997,6 +1003,15 @@ def create_session():
session_id = str(uuid.uuid4())

with sessions_lock:
# Authoritative check under the same lock as insertion — prevents
# TOCTOU race where two concurrent requests both pass the early check.
if len(sessions) >= MAX_CONCURRENT_SESSIONS:
os.close(master_fd)
try:
os.kill(pid, signal.SIGKILL)
except OSError:
pass
return jsonify({"error": f"Maximum {MAX_CONCURRENT_SESSIONS} concurrent sessions reached. Close an existing session first."}), 429
sessions[session_id] = {
"master_fd": master_fd,
"pid": pid,
Expand Down
2 changes: 2 additions & 0 deletions app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ env:
value: databricks-gpt-5-3-codex
- name: CLAUDE_CODE_DISABLE_AUTO_MEMORY
value: 0
- name: MAX_CONCURRENT_SESSIONS
value: "5"
8 changes: 4 additions & 4 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The simplest way — no CLI, no cloning, everything stays in the Databricks UI.
1. Go to **Databricks → Apps → Create App**
2. Choose **Custom App** and connect this Git repo:
```
https://github.com/datasciencemonkey/coding-agents-in-databricks.git
https://github.com/datasciencemonkey/coding-agents-databricks-apps.git
```
3. Click **Deploy**
4. Open the app — on first terminal session, paste a short-lived PAT when prompted
Expand All @@ -30,8 +30,8 @@ If you prefer working from the terminal or need more control:

```bash
databricks repos create \
--url https://github.com/datasciencemonkey/coding-agents-in-databricks.git \
--path /Workspace/Users/<your-email>/apps/coding-agents-in-databricks
--url https://github.com/datasciencemonkey/coding-agents-databricks-apps.git \
--path /Workspace/Users/<your-email>/apps/coding-agents-databricks-apps
```

### 2. Configure `app.yaml`
Expand All @@ -56,7 +56,7 @@ No secrets or resources to configure. On first terminal session, paste a short-l

```bash
databricks apps deploy <your-app-name> \
--source-code-path /Workspace/Users/<your-email>/apps/coding-agents-in-databricks
--source-code-path /Workspace/Users/<your-email>/apps/coding-agents-databricks-apps
```

> **Tip:** To update later, just `git pull` in the workspace repo and re-deploy.
Expand Down
5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "coda"
version = "0.16.7"
version = "0.17.0"
description = "CoDA - Coding Agents on Databricks Apps"
requires-python = ">=3.10"
dependencies = [
Expand All @@ -9,8 +9,7 @@ dependencies = [
"simple-websocket>=1.0",
"claude-agent-sdk",
"databricks-sdk>=0.20.0",
"mlflow-tracing>=3.4",
"opentelemetry-exporter-otlp-proto-grpc",
"mlflow-skinny==3.10.1",
"requests",
"cryptography>=46.0.6",
]
Expand Down
70 changes: 41 additions & 29 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# This file was autogenerated by uv via the following command:
# uv pip compile pyproject.toml -o requirements.txt
annotated-doc==0.0.4
# via fastapi
annotated-types==0.7.0
# via pydantic
anyio==4.13.0
Expand All @@ -20,7 +22,7 @@ blinker==1.9.0
# flask
# flask-socketio
cachetools==7.0.5
# via mlflow-tracing
# via mlflow-skinny
certifi==2026.2.25
# via
# httpcore
Expand All @@ -36,7 +38,10 @@ click==8.3.1
# via
# flask
# flask-socketio
# mlflow-skinny
# uvicorn
cloudpickle==3.1.2
# via mlflow-skinny
cryptography==46.0.6
# via
# coda (pyproject.toml)
Expand All @@ -45,19 +50,21 @@ cryptography==46.0.6
databricks-sdk==0.102.0
# via
# coda (pyproject.toml)
# mlflow-tracing
# mlflow-skinny
fastapi==0.135.3
# via mlflow-skinny
flask==3.1.3
# via
# coda (pyproject.toml)
# flask-socketio
flask-socketio==5.6.1
# via coda (pyproject.toml)
gitdb==4.0.12
# via gitpython
gitpython==3.1.46
# via mlflow-skinny
google-auth==2.49.1
# via databricks-sdk
googleapis-common-protos==1.73.1
# via opentelemetry-exporter-otlp-proto-grpc
grpcio==1.80.0
# via opentelemetry-exporter-otlp-proto-grpc
h11==0.16.0
# via
# httpcore
Expand All @@ -75,7 +82,9 @@ idna==3.11
# httpx
# requests
importlib-metadata==8.7.1
# via opentelemetry-api
# via
# mlflow-skinny
# opentelemetry-api
itsdangerous==2.2.0
# via flask
jinja2==3.1.6
Expand All @@ -93,36 +102,25 @@ markupsafe==3.0.3
# werkzeug
mcp==1.26.0
# via claude-agent-sdk
mlflow-tracing==3.10.1
mlflow-skinny==3.10.1
# via coda (pyproject.toml)
opentelemetry-api==1.40.0
# via
# mlflow-tracing
# opentelemetry-exporter-otlp-proto-grpc
# mlflow-skinny
# opentelemetry-sdk
# opentelemetry-semantic-conventions
opentelemetry-exporter-otlp-proto-common==1.40.0
# via opentelemetry-exporter-otlp-proto-grpc
opentelemetry-exporter-otlp-proto-grpc==1.40.0
# via coda (pyproject.toml)
opentelemetry-proto==1.40.0
# via
# mlflow-tracing
# opentelemetry-exporter-otlp-proto-common
# opentelemetry-exporter-otlp-proto-grpc
# via mlflow-skinny
opentelemetry-sdk==1.40.0
# via
# mlflow-tracing
# opentelemetry-exporter-otlp-proto-grpc
# via mlflow-skinny
opentelemetry-semantic-conventions==0.61b0
# via opentelemetry-sdk
packaging==26.0
# via mlflow-tracing
# via mlflow-skinny
protobuf==6.33.6
# via
# databricks-sdk
# googleapis-common-protos
# mlflow-tracing
# mlflow-skinny
# opentelemetry-proto
pyasn1==0.6.3
# via pyasn1-modules
Expand All @@ -132,8 +130,9 @@ pycparser==3.0
# via cffi
pydantic==2.12.5
# via
# fastapi
# mcp
# mlflow-tracing
# mlflow-skinny
# pydantic-settings
pydantic-core==2.41.5
# via pydantic
Expand All @@ -142,13 +141,17 @@ pydantic-settings==2.13.1
pyjwt==2.12.1
# via mcp
python-dotenv==1.2.2
# via pydantic-settings
# via
# mlflow-skinny
# pydantic-settings
python-engineio==4.13.1
# via python-socketio
python-multipart==0.0.22
# via mcp
python-socketio==5.16.1
# via flask-socketio
pyyaml==6.0.3
# via mlflow-skinny
referencing==0.37.0
# via
# jsonschema
Expand All @@ -157,6 +160,7 @@ requests @ git+https://github.com/psf/requests@bc04dfd6dad4cb02cd92f5daa81eb562d
# via
# coda (pyproject.toml)
# databricks-sdk
# mlflow-skinny
rpds-py==0.30.0
# via
# jsonschema
Expand All @@ -165,19 +169,24 @@ simple-websocket==1.1.0
# via
# coda (pyproject.toml)
# python-engineio
smmap==5.0.3
# via gitdb
sqlparse==0.5.5
# via mlflow-skinny
sse-starlette==3.3.4
# via mcp
starlette==1.0.0
# via
# fastapi
# mcp
# sse-starlette
typing-extensions==4.15.0
# via
# anyio
# grpcio
# fastapi
# mcp
# mlflow-skinny
# opentelemetry-api
# opentelemetry-exporter-otlp-proto-grpc
# opentelemetry-sdk
# opentelemetry-semantic-conventions
# pydantic
Expand All @@ -187,13 +196,16 @@ typing-extensions==4.15.0
# typing-inspection
typing-inspection==0.4.2
# via
# fastapi
# mcp
# pydantic
# pydantic-settings
urllib3==2.6.3
# via requests
uvicorn==0.42.0
# via mcp
# via
# mcp
# mlflow-skinny
werkzeug==3.1.7
# via
# flask
Expand Down
Loading
Loading