Conversation
…ared contract tests Adds 4 new tests in test_offline_runner_contract.py that prove field-for-field that Communication's NEW _build_offline_runner_env composition (shared Unity contract + hosted-only assistant-identity layer) produces dicts identical to the OLD monolithic Communication builder, across the scheduled, triggered, entrypoint-override, and sparse-assistant-data scenarios. The golden reference function is a verbatim copy of Communication's pre-refactor builder inlined into the test file. If anything in the shared contract drifts from the old behaviour, these tests fail loudly here, in Unity's test suite, before reaching Communication's deployment. Brings total contract-module test count to 35 (up from 31).
…Captcha Exposes a deterministic, Python-callable primitive `WebSessionHandle.solve_captcha()` on every web session created via `cp.web.new_session(...)`. The primitive delegates the visible reCAPTCHA v2 challenge to the AntiCaptcha worker pool and injects the returned Google-signed token back into the live page so the page's own submit flow accepts the verification. Layers wired: - agent-service: new `POST /captcha/solve` handler (sitekey extraction + createTask/getTaskResult polling + page.evaluate injection). Reads `ANTICAPTCHA_KEY` only from `process.env`; token is never logged or echoed in the response. - Python: `ComputerSession.solve_captcha` (+ matching mock-backend and `_MockSession` stubs) with rich docstring on `_LowLevelActionsMixin`. `ComputerSession._request` gains a keyword- only `timeout` parameter (default preserves existing behaviour). - Runtime exposure: `"solve_captcha"` appended to `_COMPUTER_METHODS` and `ComputerPrimitives._LOW_LEVEL_METHODS`; excluded from `_DESKTOP_METHODS` (desktop sessions have no DOM target). - Config: optional `ANTICAPTCHA_KEY` documented in `agent-service/README.md`; missing key surfaces as 503 `anticaptcha_key_missing`. - Tests: mock-backend coverage in `test_computer_multimode.py` guarding the auto-wiring and the default/invisible variant paths. Magnitude-core is intentionally untouched: the primitive is not in the LLM action vocabulary. Callers reach for it from their own orchestration code after a prior `observe()` has confirmed a CAPTCHA is on screen. Out of scope: v3/Enterprise reCAPTCHA, hCaptcha, Turnstile, FunCaptcha, GeeTest, desktop-mode equivalents, and wiring into specific actor/extractor flows.
Clean up the open-source-ready repo surface: - .gitignore now covers build/, dist/, *.egg-info/ (any name), and Local/ so setuptools/uv build output and personal workspace dirs stay out of git status. Deleted ~12MB of build/, dist/, unity.egg-info/, unify_agent.egg-info/, Local/, __pycache__/, .cache.ndjson from disk. - AGENTS.md distilled from .cursor/rules/ so Claude Code, Codex, Aider, Cline, and other assistants pick up the same conventions Cursor does (testing philosophy, no-defensive-coding, explicit-path commits, state-manager design rules, repo map). No code changes.
…anner Brings .github/ in line with peer open-source AI-assistant repos (NousResearch/hermes-agent, openclaw/openclaw) so contributors land on a familiar surface and supply-chain hygiene is visible. Added: - CODEOWNERS — @unifyai/engineers as catch-all + explicit ownership of security-sensitive paths (CODEOWNERS itself, dependabot.yml, workflows/, SECURITY.md, AGENTS.md, ARCHITECTURE.md, secret_manager/). - PULL_REQUEST_TEMPLATE.md — Summary / type / areas / test plan / migration / checklist. References the .cursor/rules invariants (no-defensive-coding, no-temporal-comments, zero-backcompat target). - ISSUE_TEMPLATE/{config,bug_report,feature_request}.yml — bug template routes by surface (CLI / voice / installer / specific manager / ConversationManager / etc.) and asks for `unity doctor` output; feature template explicitly steers users toward GuidanceManager/FunctionManager for runtime-extension requests so the issue queue isn't drowned in "please add this skill" tickets. - dependabot.yml — github-actions weekly (grouped minor/patch) + agent-service npm weekly. Deliberately skips scheduled pip updates per the editable-sibling install model (unify/unillm/orchestra-core); CVE-driven pip security updates remain enabled at the repo-settings level. Comment explains the rationale. - workflows/osv-scanner.yml — Google's reusable workflow pinned by SHA. Scans uv.lock + agent-service/package-lock.json on lockfile changes, push to main/staging, and weekly. SARIF results land in the Security tab; fail-on-vuln disabled so pre-existing CVEs don't block merges.
Lockfile bumps only — no pyproject.toml / package.json changes. Triggered by the 15 open Dependabot alerts on the default branch (see https://github.com/unifyai/unity/security/dependabot). uv.lock (7 bumps): - urllib3 2.6.3 -> 2.7.0 CVE-2026-44431 (high) cross-origin header leak in proxied redirects - urllib3 2.6.3 -> 2.7.0 CVE-2026-44432 (high) decompression-bomb bypass in streaming API - langchain-core 1.3.0 -> 1.4.0 CVE-2026-44843 (high) unsafe deserialization via overly broad load() allowlists (pulls in new transitive langchain-protocol 0.0.15) - python-multipart 0.0.26 -> 0.0.29 CVE-2026-42561 (high) DoS via unbounded multipart part headers - lxml 6.0.3 -> 6.1.1 CVE-2026-41066 (high) XXE in default config of iterparse() and ETCompatXMLParser() - langsmith 0.7.33 -> 0.8.5 CVE-2026-45134 (high) public prompt pull deserializes untrusted manifests - authlib 1.7.0 -> 1.7.2 CVE-2026-44681 (medium) OIDC implicit/hybrid open redirect (not reachable — we don't run an OIDC provider — but bumped for hygiene) - idna 3.11 -> 3.16 CVE-2026-45409 (medium) IDNA encode() bypass of CVE-2024-3651 fix agent-service/package-lock.json (2 bumps, via npm audit fix): - qs 6.15.0 -> 6.15.2 CVE-2026-8723 (medium) qs.stringify DoS on null/undefined entries in comma-format arrays - ws 8.18.3 -> 8.21.0 CVE-2026-45736 (medium) uninitialized memory disclosure Not addressed in this commit (blocked on sibling repos): - litellm 1.83.4 -> 1.83.10 (clears 4 alerts: 1 critical SQLi in proxy, 3 high — sandbox escape, RCE via MCP stdio, SSTI in /prompts/test). All four CVEs are in the LiteLLM *proxy server* surface, which Unity does not run; reachability is effectively zero, but the bump should land for defense in depth. BLOCKED: unillm pins litellm==1.83.4 exactly. The unillm Dependabot PR is already open at unifyai/unillm#54. - python-dotenv 1.0.1 -> 1.2.2 (CVE-2026-28684, medium — symlink-following in set_key; Unity only reads .env so not reachable). BLOCKED: litellm 1.83.4 ships an unusual pin (python-dotenv>=1.0.1,<1.0.1+) that effectively freezes python-dotenv at 1.0.1. Will unblock once unillm#54 lands and `uv sync` brings litellm 1.83.10 in.
The agent-service /captcha/solve handler (added in c9ba909) reads process.env.ANTICAPTCHA_KEY at request time and returns 503 anticaptcha_key_missing if it's unset. Document the env var alongside the other optional integration keys so operators know where to put it without having to read the agent-service README. The actual key value lives in GCP Secret Manager under projects/responsive-city-458413-a2/secrets/ANTICAPTCHA_KEY, alongside the other runtime API keys (ANTHROPIC_API_KEY, DEEPGRAM_API_KEY, LIVEKIT_API_KEY, etc.). The companion unity-deploy commit adds ANTICAPTCHA_KEY to setup_k8s_config.py's required_secrets list so the unity-secrets K8s Secret picks it up automatically on cluster setup.
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
The script's `discover_all()` was only recursing into top-level tests/
sub-directories whose names start with `test` — but Unity's convention
is to name per-manager test directories after the manager itself
(contact_manager/, knowledge_manager/, actor/, task_scheduler/,
conversation_manager/, etc.) without the `test_` prefix.
Effect: the staging→main CI matrix was silently collapsing to just 2
entries (tests/test_integration_status/ and tests/test_session_details.py
— the only top-level paths starting with `test`) instead of the ~67
leaf paths that actually exist. Every prior release went green on a
hollow signal exercising none of the manager test suites.
Fix: replace `item.name.startswith("test")` with
`item.name not in EXCLUDE_DIRS`. Safe because `collect_paths()` is
itself gated by `has_test_files`/`has_test_subdirs`, so recursing into
a non-test directory is a no-op. EXCLUDE_DIRS already covers
__pycache__, .pytest_cache, .venv, etc.
Verified locally: `python3 .github/scripts/discover_test_paths.py | wc -l`
returns 67 (was 2), and the output now includes tests/contact_manager,
tests/task_scheduler, tests/actor/*, tests/conversation_manager/*, etc.
…s.agent_id Two unrelated function_manager/python failures from the CI matrix that were blocking the whole cluster: 1. `uv sync` failing with "Distribution not found at: file:///tmp/.../<venv_dir>" on every test that creates a venv: The synthetic pyproject.toml generated for each user venv only declares `[project]` + `dependencies`. It is NOT an installable package. Without `--no-install-project`, uv tries to install the project itself (in editable mode), fails to find a build backend / sdist for the empty venv_dir, and surfaces "Distribution not found" — which then masquerades as a venv-creation failure. Adding `--no-install-project` to the `uv sync` invocation tells uv "install dependencies into .venv, do NOT install the project itself". The project manifest is just a dependency declaration for us, never a real package. Verified the flag exists in current uv. The CI failure was reliably reproducible across every test in the `test_venv_*` family. 2. `AttributeError: AssistantDetails ... has no attribute 'id'` in test_remote_windows fixtures: `mock_session_details_windows` and `mock_session_details_ubuntu` were calling `monkeypatch.setattr(SESSION_DETAILS.assistant, "id", "test-assistant")`. Same legacy `.id` → `.agent_id` rename that bit contact_manager tests earlier. AssistantDetails is a Pydantic model with the new field name `agent_id` (int); setting an arbitrary attribute fails because the model rejects unknown fields. Switched the fixtures to set `agent_id` to a unique int per fixture (999_001 / 999_002) to keep the two windows / ubuntu variants distinguishable in any downstream test assertions.
…gger The earlier fix (f9d2289) called caplog.set_level(INFO, logger="unity"), on the assumption that this would also subscribe caplog to the named logger. That assumption was wrong: - pytest's caplog.handler is registered with the ROOT logger only, via the `propagate=True` chain. - `caplog.set_level(level, logger=name)` sets the LEVEL on the named logger but does NOT attach caplog.handler to it. - unity/logger.py sets `LOGGER.propagate = False` (since 5ed695f, 2026-02-20 "Consolidate logging into unity.logger as single authority"), so unity log records never reach the root handler. Net effect: caplog.records stays empty for unity LOGGER output even after set_level(..., logger="unity"). Verified locally with a minimal test (`logging.getLogger("unity")` + `LOGGER.info(...)`) — without the explicit `addHandler(caplog.handler)`, the captured records list is empty; with the explicit add, the message appears immediately. Fix: explicitly `logging.getLogger("unity").addHandler(caplog.handler)` at the top of the test body, wrap the assertion block in try/finally, and `removeHandler(caplog.handler)` in finally so the handler doesn't leak across tests (each test gets a fresh caplog with a fresh handler, and a stale handler would write to a disposed buffer). The existing `set_level(..., logger="unity")` call is left as-is so the unity logger's effective level still includes INFO during the test (otherwise INFO records would be filtered before reaching any handler).
…NGS picks them up
The env vars in `pytest_configure(config)` (USER_DESKTOP_CONTROL_ENABLED,
ASSISTANT_EMAIL, ASSISTANT_NUMBER, ASSISTANT_WHATSAPP_NUMBER) were
landing too late.
Order of operations:
1. pytest collects this conftest.py → runs `from tests.helpers import ...`
at module top
2. tests/helpers.py transitively imports unity modules
3. unity.settings instantiates `SETTINGS = ProductionSettings()` —
pydantic-settings reads env vars **once**, at this point.
4. pytest_configure() runs — by now SETTINGS is frozen, so the env
overrides are silently ignored.
Symptom (caught in conv_mgr/flows + conv_mgr/voice + conv_mgr/core CI):
- test_can_you_use_my_computer: LLM answered "Not directly" instead
of "Yes — install a quick remote-access tool from unify.ai",
because SETTINGS.conversation.USER_DESKTOP_CONTROL_ENABLED was
False at SETTINGS-instantiation time → the desktop_access_faq
prompt branched the wrong way.
- test_reply_adds_re_prefix_to_subject: LLM didn't emit EmailSent
because `send_email` wasn't surfaced as a tool (gated on non-
empty assistant.email; SESSION_DETAILS.assistant.email was still
"" because ASSISTANT_EMAIL env var hadn't been read at populate
time).
Fix: hoist the env-var setdefaults to the very top of conftest.py,
BEFORE the `from tests.helpers import ...` line. Add a header comment
documenting the timing requirement so future hands don't move them
back into pytest_configure() "for tidiness". The redundant copies in
pytest_configure() stay as a defense-in-depth (in case a downstream
test reimports SETTINGS) but the authoritative point of truth is
now the module top.
…exist, restore _sync_required_contacts In commit 2b07266 I renamed the SyncContacts handler call from contact_manager._sync_required_contacts → _provision_system_overlays in both event_handlers.py and the matching test. The rename was wrong: _provision_system_overlays is NOT defined anywhere on ContactManager / BaseContactManager / SimulatedContactManager — only _sync_required_contacts is. Symptoms: - Production: every SyncContacts event raised AttributeError on cm.contact_manager._provision_system_overlays (caught and logged by the try/except in event_handlers.py:_sync_contacts, silently dropping the sync — no boom in logs, just no-op). - Tests: test_queue_operation_waits_for_initialization patched the same wrong name; production handler now AttributeError'd inside the try/except, never reached the (non-existent) overlay call, mock_sync.called stayed False, assert_called_once() failed with "Expected '_sync_required_contacts' to have been called once" (mock display name inherited from the wraps= target, which itself failed to resolve). Fix is purely the revert. Restore the actual production method name in BOTH the handler and the test. Verified locally (`pytest test_queue_operation_waits_for_initialization -xvs` → 1 passed). Lesson for future renames: confirm the target method exists on every concrete implementation (BaseContactManager, ContactManager, SimulatedContactManager) before committing the rename — `git grep "def _provision_system_overlays"` would have shown zero matches and flagged this immediately.
…n't depend on cwd test_screenshot_crop_via_act was failing with: FileNotFoundError: [Errno 2] No such file or directory: 'Screenshots' `generate_screenshot_path(entry)` returns a relative path (`Screenshots/User/<ts>.jpg`). Production code calls it from inside a worker that runs in `cwd=local_root` — see `conversation_manager/main.py:os.chdir(_local_root)` early in startup. Test fixtures spin up CM in-process via CMStepDriver without that chdir, so when the test calls `write_screenshot_to_disk` directly, the relative path resolves against whatever cwd pytest is in (usually the repo root), and `p.parent.mkdir(parents=True)` raises because `./Screenshots/` isn't there. Earlier in the test we already mkdir'd `local_root / "Screenshots" / "User"` (line 61) and the test later globs `local_root / "Screenshots" / "User"` for the written file (line 108), so the test already KNOWS where the screenshot should live — it just wasn't telling write_screenshot_to_disk explicitly. Fix: absolutise the path before the disk write: screenshot_path = str(local_root / generate_screenshot_path(entry)) This keeps the production codepath (relative + cwd chdir) untouched and makes the test independent of cwd. No prod change.
…on exit (parallel race)
ROOT CAUSE found for the function_manager/python "venv python
disappeared between prepare_venv() and create_subprocess_exec()"
RuntimeError that has been failing reliably on every CI matrix
run for weeks.
The pytest_unconfigure hook was calling
`shutil.rmtree("/tmp/unity_test_home")` at the end of EVERY pytest
session. parallel_run.sh launches one pytest session per test in
parallel tmux panes, all of them sharing the same deterministic HOME
(by design — LLM cache keys embed ~/Unity/Local and must stay
stable). When session A finishes, its pytest_unconfigure wipes the
entire tree — including the venvs that session B/C/D's
FunctionManager just created under
`$HOME/Unity/Local/.unity/venvs/<ctx>/<id>/`. The next
`execute_in_venv` in B/C/D then sees:
venv_dir=/tmp/unity_test_home/Unity/Local/.unity/venvs/<ctx>/0
exists=False
ancestor existence: False all the way up to /tmp/
The earlier diagnostic dump (commit 1e9a5f4) confirmed the wipe
scope was full-tree, not leaf-only, ruling out per-venv-id cleanup.
Fix: drop the rmtree. The shared HOME stays, but:
- CI runners are ephemeral — the dir is reclaimed when the
runner shuts down.
- Local dev: users can `rm -rf /tmp/unity_test_home` manually.
- Test isolation is already enforced by per-test Unify contexts
(each FunctionManager venv lives under a context-keyed
subdirectory), so leftover files from one test do not affect
another.
This single-line change unblocks every test in
function_manager/python and likely fixes a long tail of
intermittent "vanishing fixture file" failures elsewhere too.
… doesn't time out
Five test_remote_windows tests were failing in CI with:
RuntimeError: Managed VM did not become ready within 5 minutes
The production helper
`_execute_python_function_on_remote_windows` waits on the
`_vm_ready` threading.Event (defined at
`unity/function_manager/primitives/runtime.py:56`). That event is
set in production by either:
- ConversationManager startup
(`unity/conversation_manager/main.py:278`)
- ComputerPrimitives mock path
(`unity/function_manager/primitives/runtime.py:643`)
In a pure FunctionManager unit test neither codepath runs, so the
wait blocks until its 300s timeout fires.
Fix: pre-set the event in both `mock_session_details_windows` and
`mock_session_details_ubuntu` fixtures (the two fixtures any
remote_windows test ever uses). Capture the prior state so we
clear it again on teardown if WE flipped it on, leaving the
global event in its previous state for any sibling test that
happens to share the process.
This is the smallest surgical fix — alternatives like patching
the wait helper or refactoring `_execute_python_function_on_remote_windows`
to accept an injectable readiness signal would have a much wider
blast radius for no real benefit on the test side.
…nts, prompts, domains)
resolve_bot_token queried Orchestra with team_id and without include_token, so /slack/send could never fetch a token (400/503). Key on slack_team_id and request include_token. Add POST /slack/user-info (users.info) returning email + real/display name so the inbound pipeline can resolve an unknown Slack sender to a contact.
…name On first inbound from an unmapped slack_user_id, look up the sender via the gateway /slack/user-info and match an existing contact by email then by real/display name (ambiguity-refuse), persisting slack_user_id so later messages match directly. Addressed (@app) senders with no match get a respondable contact; others keep the gated unknown-contact policy. Breaks the bootstrap deadlock that dropped every first Slack message.
…dempotent A channel @mention is delivered twice (app_mention + message) with distinct event_ids but the same client_msg_id; dedup now keys on the stable message id so the pair collapses to one processed event. _create_slack_contact pre-checks and, on a lost unique-slack_user_id race, resolves to the existing contact instead of dropping the message.
| ) | ||
| logger.info( | ||
| "sent Slack message to %s on team %s (ts=%s)", | ||
| channel_id, |
| logger.info( | ||
| "sent Slack message to %s on team %s (ts=%s)", | ||
| channel_id, | ||
| team_id, |
…tact orphans Slack bot_user_id is workspace-scoped (on the install), so the per-assistant assistant_slack_bot_user_id is never set at bootstrap and the brain never gets the Slack send tools (always waits). Adopt the bot id from the inbound event when handling SlackMessageReceived/SlackChannelMessageReceived so the triggered brain run exposes send_slack_message/send_slack_channel_message. Also stop _create_slack_contact from minting a nameless, email-less contact that captures the slack_user_id and shadows the real contact; require a name or email and otherwise leave the sender for email/name resolution.
Surface the inbound message's event_ts as the effective thread_ts for Slack channel messages so a top-level @mention reply starts a thread instead of posting at the channel root. DMs keep prior behaviour and only thread when already threaded.
…ility demos Adds a collapsible README section illustrating six interactions the nested steerable-handle model enables (live deep redirect, live introspection, pause/inspect/resume, concurrent independent steering, clarification bubbling, and single-branch stop), each with its own generated diagram. Allow-lists the new demo images in .gitignore alongside the existing repo-shipped diagrams.
…e drifted) The container entrypoint only execed the headless offline runner when UNITY_OFFLINE_TASK_MODE == "function", but offline_runner_contract sets it to "actor" (pinned by tests/task_scheduler/test_offline_runner_contract.test_mode_is_actor). So the gate silently stopped matching: every offline scheduled/triggered task fell through to the live ConversationManager, which booted, idled on a startup reply, and exited WITHOUT executing its function — leaving the run row stale at "running" and never cloning the next recurrence. Match on any non-empty UNITY_OFFLINE_TASK_MODE so it's rename-proof. This entrypoint is baked into the unity base image; the unity-deploy/base/entrypoint.sh mirror is kept in sync.
OpenClaw and Hermes Agent both store skills as agentskills.io SKILL.md files (YAML frontmatter + markdown body + optional bundled scripts), which map near one-to-one onto GuidanceManager entries. This adds a reusable parsing/mapping core plus convenient openclaw_to_guidance and hermes_to_guidance CLIs so either library (~247 skills combined) can be imported off-the-shelf as guidance. The transfer is deliberately faithful: a skill's description + body become the guidance content and bundled scripts are inlined verbatim as textual reference (promoting them into runnable FunctionManager functions is left as a separate step). CLIs default to a dry run, are title-namespaced to avoid cross-repo collisions, and support skip/overwrite conflict handling on re-runs. Includes pure parsing tests and end-to-end GuidanceManager import tests, plus README docs.
Provide a reusable common helper for exact semantic top-k merging across multiple contexts by fetching offset+limit candidates per source, sorting by exposed score, and applying the final window once. This keeps future built-in-library reads from hand-rolling pagination and tie-breaking logic.
Raise python-multipart floor and refresh uv.lock for langchain-core, langsmith, lxml, urllib3, and python-multipart patches on main.
Resolve uv.lock conflict by re-locking on staging tip with python-multipart >=0.0.27 and upgraded transitive security patches.
Regenerate lock after unify/unillm staging updates so uv lock --check passes against CI merged state (includes litellm 1.83.7 from unillm).
fix(deps): upgrade lockfile for security advisories
Make real dashboard/data context resolution fail loud instead of silently falling back, and cover the production primitive path so dashboard tiles/layouts cannot leak into Data/Dashboards.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotes 6 commits from staging to main. Two themes plus one feature.
Open-source-readiness pass (3 commits)
aaabf3d46chore(repo): tighten .gitignore for build artifacts and add AGENTS.md.gitignorenow coversbuild/,dist/,*.egg-info/,Local/AGENTS.mddistilled from.cursor/rules/so Claude Code, Codex, Aider, Cline, etc. pick up the same conventions Cursor doesbfe44c46fchore(github): add CODEOWNERS, PR/issue templates, dependabot, OSV scannerCODEOWNERS—@unifyai/Engineersas catch-all + explicit ownership of security-sensitive pathsPULL_REQUEST_TEMPLATE.md— references the.cursor/rulesinvariantsISSUE_TEMPLATE/{config,bug_report,feature_request}.yml— routes bugs by surface; steers "please add this skill" feature requests towardGuidanceManager/FunctionManagerdependabot.yml— github-actions weekly (grouped) +agent-service/npm weekly; deliberately skips scheduled pip per the editable-sibling install modelworkflows/osv-scanner.yml— Google's reusable workflow pinned by SHA, SARIF to Security tabDependabot CVE triage (1 commit + 5 dismissals)
351563a81chore(deps): bump 9 packages to clear Dependabot CVE alertsurllib32.6.3 → 2.7.0 (CVE-2026-44431, CVE-2026-44432, both high)langchain-core1.3.0 → 1.4.0 (CVE-2026-44843 high)python-multipart0.0.26 → 0.0.29 (CVE-2026-42561 high)lxml6.0.3 → 6.1.1 (CVE-2026-41066 high)langsmith0.7.33 → 0.8.5 (CVE-2026-45134 high)authlib1.7.0 → 1.7.2 (CVE-2026-44681 medium)idna3.11 → 3.16 (CVE-2026-45409 medium)qs6.15.0 → 6.15.2 (CVE-2026-8723 medium, npm)ws8.18.3 → 8.21.0 (CVE-2026-45736 medium, npm)Plus 5 alerts dismissed out-of-band as
not_used: 4 LiteLLM proxy CVEs (Conversation Manager Refactor #69/add timestamp arg to clear_notifications #70/Summary #71/add assistant phone utterance to the thread #74 — proxy not deployed) and Replace explicit requests API calls to use Unify API #67 python-dotenv (we only read.env, never callset_key()). The litellm bump to 1.83.10 was investigated and rejected — it forcesopenai 2.30 → 2.24andlangchain-openai 1.1.15 → 1.1.10because litellm hard-pins openai. Recorded that decision via@dependabot ignore this versionon unillm#54.Net effect on the Security tab after this merge: 15 alerts (1 critical, 9 high, 5 medium) → 0.
Captcha primitive + docs (2 commits)
c9ba90982feat(computer): add solve_captcha primitive for reCAPTCHA v2 via AntiCaptcha39fe85099docs(env): document ANTICAPTCHA_KEY placeholder in .env.exampleOther in-flight work picked up incidentally
bd001c346test(task_scheduler): pin Communication env-builder equivalence in shared contract tests — landed on staging before this session.Test plan
The full test suite auto-runs on staging→main PRs (
tests.ymlline 130). No tags needed. Auto-merge on green.