Conversation
#1231) (#1233) Agent containers mounted /tmp as a hardcoded 100 MB noexec,nosuid RAM-backed tmpfs. It fills easily — e.g. `gh` CLI install artifacts (~38 MB) that hardcode /tmp and bypass the #1098 TMPDIR redirect — after which every /tmp write fails with "No space left on device", including git's commit scratch, so autonomous scheduled runs "complete" but silently fail to persist. The size being a literal meant operators couldn't tune it without a code change + base-image rebuild. - capabilities.py: AGENT_TMPFS_MOUNT size now read from AGENT_TMP_SIZE (env on the backend service, which builds the agent mount spec), default 512m, validated `^\d+[mg]$` with empty/invalid → default. noexec,nosuid stay hardcoded — only size is configurable, and it stays bounded (counts against the agent memory cgroup). Single source of truth, so create (crud.py) and recreate (lifecycle.py) can't drift. - Wire AGENT_TMP_SIZE=${AGENT_TMP_SIZE:-512m} on the backend service in both docker-compose.yml and docker-compose.prod.yml; document in .env.example. - architecture.md Container Security: note the now-configurable size. - tests/unit/test_1231_agent_tmp_size.py: default, valid m/g, case-fold, invalid→default, and the security flags are never dropped. Mount specs are creation-time: existing agents pick up a new size on recreate, not restart. Builds on #1098 (TMPDIR redirect) — closes the gap for tools that hardcode /tmp. The agent-side git-sync silent-persist-failure is a separate issue in the abilities repo, per the ticket. Related to #1231 Co-authored-by: Eugene Vyborov <1073874+vybe@users.noreply.github.com>
…#1149) Surface per-schedule performance on the Overview tab and the Schedules tab, both from a SINGLE compact aggregate (no N per-schedule round-trips) — extends #1107 (Overview) and generalises #868 (per-schedule deep analytics). Backend: - db `get_agent_schedules_summary(agent, hours)` — one rollup row per non-deleted schedule (zero-run schedules included): terminal success_rate (success / (success + failed[incl. error]); None when zero terminal), NULL-skipping avg_duration_ms, cost_total, context_avg, tool_call_total (parsed over newest 5,000 rows agent-wide, tool_calls_sampled flag), and last-run outcome. Cheap grouped SQL; iso_cutoff window (Invariant #16). - GET /api/agents/{name}/schedules/analytics-summary?window=7d|14d|30d (AuthorizedAgent). Declared BEFORE /{schedule_id} in routers/schedules.py so the literal segment isn't captured as a schedule_id (Invariant #4) — putting it in analytics.py would be shadowed (schedules_router mounts first). - models: ScheduleSummaryRow + AgentSchedulesSummaryResponse (Invariant #14). Frontend (single fetch, two consumers — Invariant #7): - executions.js fetchSchedulesSummary, cached per ${name}:${window} like fetchAgentAnalytics. - OverviewPanel: "Schedules performance" section, honors the existing 7/14/30d window selector, each row deep-links to the Schedules tab; hidden at zero. - SchedulesPanel: inline mini-stats per row (success rate, avg duration, runs, last-run dot) — badge style, no new chart/modal — from the same call. Tests: tests/unit/test_1115_schedules_summary.py (6) — terminal success rate, NULL-skip avg, tool-call total, zero-run-still-appears, soft-delete excluded, out-of-window excluded. Full analytics suites green (30 passed). Frontend prod build clean; endpoint verified live across 7/14/30d windows. Related to #1115 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Eugene Vyborov <1073874+vybe@users.noreply.github.com>
…igration (#1290) Adds a top-of-README callout recommending PostgreSQL for production (SQLite remains the zero-config dev default, opt-in via DATABASE_URL, #300), links the public Trinity Ops Agent (trinity-ops-public) for instance operations, and documents migrating existing SQLite instances via its /migrate-to-postgres skill. Also adds a Database section, a DATABASE_URL env row, and an ops-agent entry in the docs index. Co-authored-by: Eugene Vyborov <eugene@beingluminous.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r into one shared module (#1088) (#1297) The `is_auth_failure` + `AUTH_INDICATORS` + `NON_AUTH_KILL_MARKERS` (#904) logic was duplicated inline in `subscription_auto_switch.py` and `scheduler/service.py`, kept in sync by a hand-written "keep these lists in sync" comment — exactly how the #904 kill-marker bug class re-appears. Consolidate into one canonical module: - New `src/backend/services/failure_classifier.py` — canonical, pure-stdlib classifier (55 lines). `subscription_auto_switch.py` now re-exports `is_auth_failure` unchanged, so existing importers (`routers/chat.py`, `services/task_execution_service.py`) and their test patch targets keep working. - New `src/scheduler/failure_classifier.py` — byte-identical vendored mirror. The scheduler runs in a separate container and cannot import `backend.services`; it uses the classifier for log-labelling only (picks the `logger.error` wording, never gates a switch). The agent-runtime classifier in `error_classifier` is intentionally NOT merged — it diverges semantically and stays kill-safe by `_classify_signal_exit` precedence (D4). - Byte-identity is enforced by `tests/unit/test_904_sigkill_no_false_auth.py::TestBackendSchedulerParity`; the re-export is pinned by `TestBackendReExportGuard`. No hand-sync. Pure structural refactor, no behavioral change (verified by SHA-256 equality of the two copies and line-for-line comparison vs the deleted code). The test rewrite also drops the prior `exec(compile(...))` source-slicing in favour of `importlib` path-loading, removing the only injection primitive in scope. Tests: 19/19 pass in `test_904_sigkill_no_false_auth.py`. CSO --diff: CLEAR (docs/security-reports/cso-diff-2026-06-21.md). Refs #1088 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nd default-OFF flag (#946) (#1293) * feat(orchestration): pull-pilot routing for agent→agent MCP chat behind default-OFF flag (#946) Phase 2 PoC for pull/work-stealing (Epic #1045, umbrella #1081). When MCP_AGENT_CHAT_PULL_ENABLED is ON, a sequential agent→agent (scope='agent', non-self) chat_with_agent is routed by the MCP server through the durable async /task path instead of the synchronous held /chat; the caller gets an immediate {accepted|queued, execution_id} receipt and polls get_execution_result. scope='user', self-tasks, and parallel=true are unchanged. Default OFF — flag flip + MCP routing revert is the whole rollback. - config.py: canonical MCP_AGENT_CHAT_PULL_ENABLED registry entry (both services read the SAME env key, so a single-.env deploy can't drift). - settings.py: surface mcp_agent_chat_pull_enabled in /api/settings/feature-flags (auth-gated, observability-only — not a UI surface). - chat.py: release the idempotency claim on the two /task dispatch-breaker-open (CircuitOpen) deny paths, mirroring /chat and CapacityFull (T5 fix) — without it a breaker-open reject silently blocks same-key retries for 24h. - mcp-server: scope-based pull routing + D8 dispatch-mode idempotency token so a flag flip can't replay a wrong-shape snapshot across endpoints; startup log of the routing mode for the soak's control/treatment window. - tests: chat.test.ts (routing fork + key behavior), test_946_task_idempotency_on_deny.py (deny-path claim release), feature-flag exposure tests. - docs: ACTOR_MODEL_POSTCARD (#945 resolved), PULL_PILOT_946_SOAK harness + go/no-go record, CSO diff audit (CLEAR), TARGET_ARCHITECTURE/architecture updates. Refs #946 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(feature-flows): document pull-pilot routing (#946) + AGENT_TMP_SIZE tmpfs (#1231) Sync feature-flow docs with recent changes: - agent-to-agent-collaboration.md: new Pull-Pilot Routing (#946) section — flag-gated MCP routing fork to the durable async /task path, poll-for-result receipt contract, D8 idempotency route token, feature-flag exposure, and the T5 /task dispatch-breaker-open deny-path claim release. - container-capabilities.md: refresh stale tmpfs facts — agent /tmp size is now operator-configurable via AGENT_TMP_SIZE (default 512m, noexec,nosuid fixed), plus the TMPDIR=/home/developer/.tmp heavy-scratch redirect (#1098). - feature-flows.md: add Recent Updates index rows for #946 and #1231. Refs #946 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Eugene Vyborov <eugene@beingluminous.com>
…1294) * feat(agent): runtime data_paths with portable export/import (#1169) Declare an agent's runtime data (SQLite DBs, datasets) under data/ on the already-durable home volume — no separate volume, no platform schema change. - template.yaml `data_paths:` surfaced by template_service (github + local) and materialized at creation by crud.py -> git_service.materialize_data_paths: writes ~/.trinity/data-paths.yaml and appends data/ + entries to the agent's own .gitignore (idempotent). Opt-in; empty list is a no-op. - S4 persistent-state and data_paths now share one extracted heredoc/list primitive (materialize_trinity_yaml_list / _read_trinity_yaml_list). - New routers/agent_data.py: POST /data/export (stream | base64, 413 over cap, manifest-only tar when data/ missing) and POST /data/import (proxies to the agent-server restore primitive; data/** allowlist + traversal guard; Idempotency-Key). Both serialized per agent by a cross-worker Redis op lock. - MCP tools export_agent_data / import_agent_data (Invariant #13). - Validation checks DP-001..DP-005 in agent-validation-spec. - Docs: architecture, requirements, feature-flows index + agent-data-volumes flow, agent guide; CSO diff audit report (0 critical/high). Tests: ~30 unit + TestClient tests (export/import endpoints, allowlist, gitignore, template surface). PR2 (scheduled snapshots + pre-snapshot quiesce hook + retention/cascade) deferred. Closes #1169 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(agent-data): satisfy sys.modules pollution lint in #1169 tests The two new data_paths test files copied the baselined `patch.dict` + bare `del sys.modules[...]` loader from test_persistent_state_allowlist.py, which the sys.modules pollution lint flags as NEW (non-baselined) violations. Adopt the blessed snapshot/restore exception (precedent: test_telegram_webhook_backfill.py): declare a top-level `_STUBBED_MODULE_NAMES` list + an autouse `_restore_sys_modules` fixture, and install the stubs / evict the cached module directly (the fixture owns restoration). Removes the bare `del sys.modules[...]` entirely rather than hiding it. Drop the now-unused `patch` import from the gitignore test. Lint passes (no new violations); both files' 16 tests still green; no cross-file leakage. Refs #1169 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(feature-flows): sync recent changes — #1231 tmpfs, #1115/#1231 index rows Fix container-capabilities.md for the now-configurable agent /tmp tmpfs (#1231): default 100m → 512m via AGENT_TMP_SIZE, and correct the stale full_capabilities ternary excerpts to the shared AGENT_TMPFS_MOUNT constant (noexec,nosuid always applied, both modes). Add Recent Updates index rows for the per-schedule performance scorecards (#1115) and the tmpfs-size fix (#1231); the #1169 and #1116 rows already shipped in-commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Eugene Vyborov <eugene@beingluminous.com>
…ed agent network (#1159) (#1292) * fix(security): authenticate the in-container agent server on the shared agent network (#1159) The in-container agent server (:8000) had zero inbound auth on trinity-agent-network: any agent could read a sibling's .env secrets or run arbitrary Claude on it. Every backend->agent call now carries a per-agent X-Trinity-Agent-Token = HMAC-SHA256(AGENT_AUTH_SECRET, "trinity-agent-auth:v1:"+name), verified by a pure-ASGI middleware on all HTTP and WebSocket routes (constant-time compare; only /health exempt). - Derive-don't-store: the master AGENT_AUTH_SECRET lives only in the backend env (auto-generated by start.sh like SECRET_KEY); each container receives only its own token, so a compromised agent cannot compute a sibling's. Fail-closed -- derive raises on an empty secret. - Callers route through services/agent_auth.py (agent_httpx_client / build_agent_auth_headers / merge_auth_headers); a static guard test fails any new raw agent-{name}:8000 caller that bypasses them. - Removed the dead, unauthenticated /ws/chat route (ran arbitrary Claude) and the agent server's wildcard CORS (internal-only). - Grace path for old images: empty TRINITY_AGENT_AUTH_TOKEN -> allow; check_agent_auth_token_env_matches forces a one-pass recreate to inject. - Retired the unused src/scheduler/agent_client.py. Tests: unit (matcher, middleware, header guard, derivation) + security isolation test. CSO diff audit in docs/security-reports/cso-diff-2026-06-20.md. Closes #1159 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(feature-flows): document agent-server authentication (#1159) + sync recent flows Add a feature-flow doc for the in-container agent-server inbound auth shipped in this PR, and sync the index with two recent merged changes. - New feature-flows/agent-server-authentication.md: end-to-end trace of the derived X-Trinity-Agent-Token (HMAC over AGENT_AUTH_SECRET), the pure-ASGI middleware enforcing it on every HTTP/WS route, fail-closed vs grace path, recreate reconciliation, and the migrated callers + static guard. Added to the Authentication & Security catalog in the index. - container-capabilities.md: refresh the stale /tmp tmpfs size (was a hardcoded 100m) to the configurable AGENT_TMP_SIZE (default 512m, #1231). - Recent Updates rows for #1159, #1231, and the previously-missing #1115. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Eugene Vyborov <eugene@beingluminous.com>
#1039 (configurable data-retention) moved to the private enterprise tracker; replace the now-private issue number in voip-telephony.md with a generic description so the public doc doesn't deep-link a private issue. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…runner-1160 docs(feature-flows): add database-migration-runner flow (#1160)
Run ~100 best-practice checks (11 categories) against a running agent's workspace, surfaced non-blocking in the Overview tab with one-click auto-fix for the 10 gitignore checks, plus an MCP tool. - services/compatibility/ package (spec/collector/static_checks/ai_checks/fixes): ONE docker exec -> in-container python -> JSON snapshot (secret files existence-only, size/binary caps); pure STATIC checks (HARD-only) + category-batched AI checks (Haiku, iterate-expected, fail-open, capped at SOFT, secret-redacted); runtime-aware (claude-only checks skipped for Codex/Gemini). - GET/POST endpoints (read AuthorizedAgentByName; fix OwnedAgentByName, gitignore only, per-agent Redis lock, atomic write, uncommitted until next sync; include_ai path rate-limited). agent_compatibility_results table (dual-track SQLite + Alembic) persists the latest snapshot; cascade/rename via AGENT_REFS. - CompatibilityPanel.vue (two-phase fetch, grouped checklist, per-check fix, re-run) in OverviewPanel; get_agent_compatibility_report MCP tool. - 35 fixture-driven unit tests; spec sync-tested against docs/agent-validation-spec.md. Persistence departs from the issue's "no DB table" note so AI verdicts show without re-spend + enable fleet aggregation (see requirements section 41). Fixes #668 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ity bumps, CI shims, license, CSP) # Conflicts: # .github/workflows/container-security.yml # .github/workflows/schema-parity.yml # src/frontend/security-headers.conf # src/frontend/vite.config.js # tests/git-sync/package-lock.json # tests/git-sync/package.json
chore: reconcile origin/main into dev (14 out-of-band commits)
CodeQL py/polynomial-redos (high): `_ASSIGN_RE` captured the value as `[ \t]*(.+?)[ \t]*$`. The lazy `.+?` and the surrounding `[ \t]*` can both match a tab, giving polynomial backtracking when `_redact()` runs the pattern over up to 48 KB of agent-supplied file text. Capture the value greedily to end-of-line (`(.*)$`) and let the callers strip — both `_looks_placeholder()` callers already `.strip()`, so secret detection and redaction are behaviourally identical (verified: `=`, `:`, `export`, and indented forms still match). 35 unit tests pass.
feat: agent deployment compatibility validation — server-side checks with auto-fix (#668)
Companion to trinity-enterprise#36. OSS carries only the entitlement-gated
surface; all SSO logic lives in the private submodule.
- Login.vue: "Sign in with <IdP>" buttons (shown only when the `sso` feature is
entitled and a provider is enabled), plus OIDC callback-fragment handling
(`/login#sso=ok|mfa|error`) — reuses the existing 2FA challenge UI when the
IdP login still requires a local second factor.
- stores/auth.js: completeSsoLogin() (reuses _finalizeLogin / _setMfaChallenge)
+ fetchSsoProviders() (empty in OSS-only builds — endpoint 404s).
- Settings.vue: admin-gated "SSO" tab → SsoPanel.vue (provider CRUD + test +
policy). Gated by enterpriseStore.isEntitled('sso'), same as the 2FA tab.
- Bump enterprise submodule to the SSO module commit.
- docs: architecture enterprise-modules row + requirements §40 (SSO/OIDC).
No new backend dependency (python-jose + httpx already in the image) and no
OSS Python changes — the mint/whitelist/mfa seams already exist.
Stacked on feat/5-2fa-totp (reuses the OSS mfa_gate + 2FA challenge surface).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pulls in the email_verified / issuer-pinning / login-CSRF fixes (trinity-enterprise 87c8f97). OSS gated surface unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…flow Add an "Incubating Directions (Not Yet Decided)" section to TARGET_ARCHITECTURE.md capturing the goal-directed control-surface idea (Objective + policies + roster + externally-measured evals), explicitly bounded by CLAUDE.md §8 and sequenced after the pull migration + #300. Incubating in trinity-enterprise#27. Reword the voip-telephony flow note to drop a stale #1039 reference in favor of describing the LOG_* data-retention no-op class directly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Routine /cso full-audit posture report (Phases 0–14, daily 8/10 gate). Follows the docs/security-reports/ convention; no real secrets reproduced. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ync dev features - Add videos.md (35 published videos, newest-first by topic) and a README Watch section - Add 'Watch' callouts to 32 feature pages linking the most relevant, newest videos - Add user-facing whats-new/v0.6.1.md (translated from release notes; no issue numbers) - Document dev-only features: agent runtimes (Claude Code/Codex/Gemini CLI, #1187), agent data paths + export/import (#1169), compatibility validation (#668), in-app bug reporting (#1116), subscription hot-reload (#1089), pull-pilot routing (#946), configurable AGENT_TMP_SIZE (#1231), Postgres migration-runner groundwork (#1160) - Index agent-runtimes, agent-data, and the previously-orphaned agent-session pages Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/trinity-enterprise#38, #82) Capture an optional operator email/company at first-run setup with an explicit, unchecked-by-default opt-in to "occasionally receive important security & product updates", submitted once to a new /v1/operator-intake endpoint on #1116's Cloudflare intake app. The same email binds as the admin's sign-in identity so the operator can log in with email + password — no verification email is sent (a fresh install has no Resend key; the email is bound, not code-verified). The code-based email second factor stays Phase 2 on the existing mfa_gate seam. - backend: operator_intake_service (fire-and-forget, at-most-once via a system_settings marker, DO_NOT_TRACK aware, owns installation_id); setup endpoint captures profile + binds admin email; authenticate_user resolves the admin by username OR registered email (password guard blocks code-only users); PUT /api/users/me/email for the existing-admin transition - frontend: SetupPassword email/company + consent checkbox; Login "username or email" field; Settings -> General "Admin sign-in email" card - config: OPERATOR_INTAKE_ENABLED / OPERATOR_INTAKE_URL (+ .env.example) - docs: requirements section 43, architecture catalog, first-time-setup feature flow - tests: 16 unit tests (intake idempotency/guards, email-login resolution, setup) Fixes abilityai/trinity-enterprise#38 Fixes #82 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…in (trinity-enterprise#38, #82) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#82) The _EMAIL_RE pattern duplicated into setup.py and users.py had two [^@\s]+ atoms around the literal \. that both also match '.', giving the engine many ways to place the dot and backtracking polynomially on user-controlled email input (CodeQL alerts #211, #212). Constrain only the final segment to [^@\s.]+ (no dot) so the trailing \. can align with exactly one position -> linear matching. Behaviour is unchanged: multi-subdomain addresses still validate; an 80k-char pathological input now resolves in ~2ms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…erts, SSH, binary) (#1305) * feat(credentials): curated credential file-type injection — SA keys, certs, SSH, binary (enterprise#11) Widens CRED-002 injection from the fixed 3-path exact allowlist (.env/.credentials.enc/.mcp.json) to a curated set of credential file *types*, without reopening the arbitrary-path RCE surface (#183/#590/#598). - New services/credential_paths.py — single-source policy: ALLOW (.config/gcloud/**, .kube/config, *.pem/*.key/*.crt/*.cert/*.p12/*.pfx, .ssh/id_*, + existing exact set) with deny-precedence over anything executed/sourced at startup (shell rc, CLAUDE.md/AGENTS.md/.claude/**, .mcp.json.template, .ssh/authorized_keys/config, .git*, bin/**) and `..`/absolute traversal. Vendored byte-identically into the agent image (Invariant #5) with a parity test. - Agent-server hardening: the inject + update file loops now enforce the policy AND a resolve-under-home traversal guard the original write path lacked; parent-dir creation + chmod 0o600 preserved. New GET /api/credentials/list for export discovery. - Binary-safe: inject carries files_b64 (base64); agent writes via write_bytes. .credentials.enc gains a v2 {files, files_b64} envelope (legacy flat archives still decrypt); encrypt/decrypt stay flat for the single-secret callers (SIEM/2FA/SSO). - Export now captures the FULL injected set (via /list) + binary, not just the 2 defaults. - Three surfaces in sync (Invariant #13): MCP inject_credentials gains files_b64; frontend CredentialsPanel gains a file-upload affordance (text vs base64 auto-detected). - Tests: allowlist test now exercises the REAL policy (newly-allowed + still-blocked), + credential_paths parity test + binary archive round-trip test. 61 pass. - docs/memory/architecture.md: credential-path policy documented. Related to Abilityai/trinity-enterprise#11. Loosens a deliberately-tight boundary — run /cso on the diff before merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(credentials): close /cso findings on the injection widening (#11 review) Security review of the widening surfaced one HIGH regression + hardening items; all fixed here. #1 (HIGH, RCE): the #598 .mcp.json content-validation guard was bypassable via the new files_b64 (binary) channel — validate_mcp_config only checked `files`, so `files_b64={".mcp.json": base64(<stdio-command MCP server>)}` skipped it and configured an RCE MCP server on the target agent. Fix: .mcp.json may only arrive as TEXT (files), where it is validated; rejected in files_b64 at the backend inject router AND the agent-server write helper. #2 (defense-in-depth): import/auto-import wrote decrypted archives via the agent-server /inject layer only. Added validate_credential_set() (curated path policy + .mcp.json content + no-binary-.mcp.json) on the backend import boundary so enforcement is dual-layer as the issue mandates. (Archives are AES-GCM with the server key, so a forged archive wasn't practical — but the layer belongs.) #3: .ssh/ is now locked to id_* only — a stray *.key/*.pem under .ssh is no longer accepted (policy was previously broader than the "SSH keys = id_*" intent). #4 (noted): .config/gcloud/** can hold a google-auth executable credential_source; only honored under non-default GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES=1. Documented in credential_paths.py. +6 regression tests (169 pass). CSO report: docs/security-reports/cso-2026-06-22-11-diff.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(credentials): exclude vendor dirs from cert globs + accurate export count (#11 live test) Found while testing PR #1305 against a real local instance: 1. Over-capture: the broad *.pem/*.key/*.crt globs matched bundled CA files (e.g. .local/.../site-packages/certifi/cacert.pem), so export's /list walk swept vendored cert material into .credentials.enc. Added node_modules, site-packages, .local, .venv/venv, .cache, go/pkg to the deny-list (both root and nested forms) so cert globs only catch real credential files. 2. export's files_exported count re-read just the 2 default files (reported 1 while the archive actually held 5). export_to_agent now returns the true captured count; dropped the redundant stale read. Verified end-to-end on a live agent: allowed types inject (text+binary, 0600, parent dirs), blocked paths 400 (incl. .ssh non-id_*, .mcp.json-via-files_b64, weaponized .mcp.json text), and binary round-trips through export→import with matching sha256. +5 regression tests (72 pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dmin-email feat(setup): first-run operator intake + admin email login (trinity-enterprise#38, #82)
…ev-sync docs(user-docs): video library + per-page links, v0.6.1 What's New, sync dev features
…-ref docs(voip): genericize moved-issue reference in feature-flow
…stale voip hunk
Two review-driven fixes ahead of the v0.7.0 cut:
- Annotate the CSO posture report (.md + .json) with post-audit remediation
status. The report audited `main` pre-cut and listed F1/F2/F3 as open
VERIFIED findings; they are already remediated on `dev` and ship in v0.7.0:
- F1 (unauth agent-server) -> #1159 X-Trinity-Agent-Token middleware
- F2 (fastmcp -> hono/undici) -> #1255, #1289; fastmcp ^4.3.0
- F3 (form-data CRLF via axios) -> #1254
- F4/F5/F8 exploit path closed by #1159 (auth gate)
Adds a top-of-report banner, per-row status tags, per-finding notes, and a
machine-readable `remediation_status` block in the JSON. Avoids publishing a
stale "open CRITICAL + exploit" to a PUBLIC repo without its fix context.
- Drop the voip-telephony.md reword: it is superseded by already-merged #1301,
which made the identical `#1039` -> `LOG_*` change on `dev`. Restoring to
merge-base removes the redundant/conflicting hunk from this PR.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs: incubating direction + voip flow reword + CSO 2026-06-21 report
test: fix dev test-suite drift + harness bugs (no product changes)
…catalog fix(templates): curate the agent template catalog — hide fixtures, order starters first (#1513)
feat: bundle a Brain-Orb-enabled Cornelius in the default install (Abilityai/trinity-enterprise#107)
…l-height # Conflicts: # docs/memory/feature-flows.md # docs/memory/learnings.md
…ntext-split # Conflicts: # CLAUDE.md
…trinity-enterprise#78) Builds on the portal chat drawer: - Chat sessions: per-agent history switcher + "New chat"; resumes the most-recent thread on open; search results open on their thread. - Cross-chat search: a search box on the portal home searches the client's conversations (title + message content) across all rostered agents; results replace the roster and open the matching thread. - Voice mode: 🔊 speaks replies (ElevenLabs TTS) and a 🎤 mic dictates input — browser Web Speech API where available (Chrome/Edge/Safari), MediaRecorder → server Scribe STT fallback for Firefox. Gated on the agent's `voice_available`. - Polish: larger, clickable agent cards on the roster; a "How do files work?" note in the Files drawer. Backed by the enterprise endpoints in trinity-enterprise#106 (sessions/search/ tts/stt). Frontend-only; no OSS submodule bump here (lands after #106 merges). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bumps the patch-and-minor group in /src/mcp-server with 3 updates: [fastmcp](https://github.com/punkpeye/fastmcp), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [tsx](https://github.com/privatenumber/tsx). Updates `fastmcp` from 4.3.2 to 4.4.0 - [Release notes](https://github.com/punkpeye/fastmcp/releases) - [Commits](punkpeye/fastmcp@v4.3.2...v4.4.0) Updates `@types/node` from 26.0.1 to 26.1.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `tsx` from 4.22.4 to 4.23.0 - [Release notes](https://github.com/privatenumber/tsx/releases) - [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs) - [Commits](privatenumber/tsx@v4.22.4...v4.23.0) --- updated-dependencies: - dependency-name: fastmcp dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: patch-and-minor - dependency-name: "@types/node" dependency-version: 26.1.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: patch-and-minor - dependency-name: tsx dependency-version: 4.23.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: patch-and-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the patch-and-minor group in /tests/git-sync with 1 update: [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). Updates `vitest` from 4.1.9 to 4.1.10 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.10/packages/vitest) --- updated-dependencies: - dependency-name: vitest dependency-version: 4.1.10 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-and-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the patch-and-minor group in /src/frontend with 3 updates: [js-yaml](https://github.com/nodeca/js-yaml), [vue-chartjs](https://github.com/apertureless/vue-chartjs) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `js-yaml` from 5.2.0 to 5.2.1 - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](nodeca/js-yaml@5.2.0...5.2.1) Updates `vue-chartjs` from 5.3.3 to 5.3.4 - [Release notes](https://github.com/apertureless/vue-chartjs/releases) - [Changelog](https://github.com/apertureless/vue-chartjs/blob/main/CHANGELOG.md) - [Commits](apertureless/vue-chartjs@v5.3.3...v5.3.4) Updates `vite` from 8.1.2 to 8.1.3 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.1.3/packages/vite) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 5.2.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor - dependency-name: vue-chartjs dependency-version: 5.3.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor - dependency-name: vite dependency-version: 8.1.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-and-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…'t flake (#1500) The frontend-e2e run surfaced a second pre-existing infra flake (alongside the #954 spec repair): dashboard-grid-view timed out clicking the dashboard mode toggles because the first-run OnboardingWizard (trinity-enterprise#52) auto-opens on the CI stack's fresh, zero-user-agent Dashboard and its `fixed inset-0` backdrop intercepts pointer events. It's a race against fleet-load, so it was flaky, not deterministic, and local dev never reproduces it (a real stack has agents, so the wizard never auto-opens). Seed the product's own remembered-dismissal key (`trinity_onboarding_dismissed_v1`) in the shared auth.setup.js storageState — same origin as the JWT `token`, so it's captured by the snapshot every spec inherits. Fixes it fleet-wide with one line; specs that want the wizard opt back in via `?onboarding=1` (which bypasses the key by design). No app code changes. Adds a learnings entry for the fresh-install-only-modal class of e2e flake. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…wnership (#1478) (#1509) LOG_RETENTION_DAYS archived old log files but never freed disk: the backend mounted the logs volume read-only, so log_archive_service.unlink() failed per file (`Errno 30: Read-only file system`) and the volume grew unbounded (~900MB/day; eu2 hit 32G). Two layers were blocking the delete: 1. **Read-only mount** — docker-compose.prod.yml AND docker-compose.yml both mounted `trinity-logs:/data/logs:ro` on the backend. Changed to read-write. Vector stays the sole writer of new log lines; the backend only reads (to gzip) + deletes the archived originals. 2. **Directory ownership** — Vector runs as root and creates `/data/logs` root:root 0755, so even rw the backend (UID 1000) can't unlink (deleting a file needs write on the *directory*). Vector can't fix it itself (cap_drop: ALL → no CAP_CHOWN). Added a one-shot `logs-init` container (root, alpine) that `chown 1000:1000` + `chmod 775` the dir before the backend starts (depends_on: completed). Chowns the dir only (fast, not -R); Vector's future root-owned files stay deletable via the now-writable dir. Also: log_archive_service.start() logs a WARNING at startup if LOG_DIR isn't writable, so the failure is visible instead of silent per-file errors at cleanup_hour. Verified live (dev): reproduced the `:ro` failure; after the fix the dir is 1000:1000 775, backend writes, and a root-owned file in /data/logs is deletable by UID 1000 (Errno 30 gone). Closes #1478. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plit docs(claude-md): split core-team context into .claude submodule via @import
…7-08 docs(user-docs): sync dashboard Grid view + fork-to-own; fix broken links
fix(ui): Tasks tab fills viewport height like the Chat tab (#1500)
…d (trinity-enterprise#78) Point src/backend/enterprise at feature/78-portal-history-hardening (630cca9), which carries the client-portal sessions/search/voice endpoints this frontend (#1498) calls. Forward move from f1bafee. Re-bump to the merged commit once trinity-enterprise#106 lands on enterprise main. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(portal): Client Portal frontend — chat, sessions, search, voice, files (trinity-enterprise#78)
Replace stale (old-nav) screenshots with the current v0.7.0 UI and refresh the docs/README to reflect features shipped since 0.7. Screenshots - Add current-UI shots (Grid dashboard, Operations->Executions, Agent Overview, Brain Orb) to the README showcase and the previously image-less user-docs sections (dashboard.md Grid view, executions.md Operations tab, managing-agents.md Overview tab, dynamic-dashboards.md Brain Orb). - Remove 4 orphaned old-nav README assets; add a !docs/assets/screenshots/*.png gitignore negation so README assets track without force-add. User docs - Document the fresh-install default Cornelius agent (setup.md). - Document the Starter Templates catalog curation + hidden fixtures (creating-agents.md). README feature refresh (post-0.7) - Runtimes: add OpenAI Codex; Fleet Observability: add Grid View. - Add Sequential Agent Loops, Webhook Triggers, Voice, Operator Queue, Agent Overview, and Compatibility Report. - MCP: tool count 74 -> 90+; add missing categories (git, operator-queue, loops, reports, memory, pipelines, voip). - Fix stale "Events page" -> Operations page. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-readme-refresh docs: refresh screenshots + user docs + README for post-0.7 features
…s created_at (#1426) (#1435) * fix(operator-queue): tolerate agent items missing created_at/title/question Agents author ~/.trinity/operator-queue.json free-form. An item that omitted created_at made OperatorQueueOperations.create_item raise KeyError; because the item stayed `pending` in the agent file, the 5s operator-queue sync retried and error-logged it EVERY cycle, indefinitely ("Failed to create queue item <id>: 'created_at'" on eu2/v0.7.0) — the request never reached the Operating Room and the operator never saw it. title/question had the same latent KeyError. Make the sync ingest boundary defensive: default the three hard-indexed fields in create_item (mirroring the existing type/status/priority .get() defaults). created_at defaults to utc_now_iso() (ingest time, ISO-Z per Invariant #16); title/question get sensible fallbacks. The item is now created once, so the next cycle sees it via exists() and the loop stops — the request surfaces instead of vanishing. Verified: - tests/unit/test_1426_operator_queue_created_at.py (3, real code + SQLite): missing created_at defaulted (not raised); missing title/question defaulted; present fields preserved. - Live against the rebuilt backend image + real DB: a created_at-less item creates without KeyError (created_at defaulted to now), and the 5s sync loop logged zero "Failed to create queue item" errors. Related to #1426 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(test): #1426 test used an uninitialized temp DB → regression-diff CI fail The test overrode TRINITY_DB_PATH to a fresh file, but init_database() runs only once (at first `database` import, under the conftest-pinned path), so get_engine() then hit an uninitialized DB — every case failed with 'no such table: operator_queue', which the regression-diff job flagged as a head-only regression. Use the conftest's already-initialized per-process DB (drop the override). The operator_queue.py fix itself is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Eugene Vyborov <1073874+vybe@users.noreply.github.com>
…nc model catalog (#1521) (#1524) Replace ~15 scattered hardcoded 200000 context-window denominators with one model→context-window catalog so the "% context used" gauge is correct for 1M-context models (Opus 4.8/4.7, Sonnet 5, Fable 5, Gemini = 1M; Codex = 272K). The runtime-reported modelUsage.contextWindow stays PRIMARY; the catalog is the FALLBACK only — bare Claude → 200K safe floor, [1m] → 1M, Gemini → 1M, Codex → 272K, unknown → 200K + logged warning. The bare-Claude floor is deliberate: the effective window is auth/plan-dependent, so a fixed 1M fallback would hide an imminent 200K compaction wall. - New pure-stdlib services/model_context.py, vendored byte-identically into the agent server (Invariant #5, parity-tested). - Unify 5 disagreeing agent-server resolvers (state.py, claude_code, gemini, codex, the ABC) via the catalog. Seed metadata.context_window at construction in the Claude + headless paths — the real Claude fallback, since get_context_window is dead code for Claude and the flat Pydantic default was the actual fallback. Fix state.py init-ordering so a [1m] agent reports 1M before its first turn. The non-abstract ABC default means a new runtime can't ship a silently-wrong 200K. - Backend downstream fallbacks use the shared DEFAULT_CONTEXT_WINDOW constant. - Re-sync ModelSelector presets (add Fable 5, Sonnet 5) + agent-server alias note. - No schema change (columns/DDL unchanged; only written values become correct). Co-authored-by: Eugene Vyborov <eugene@beingluminous.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Oleksii Dolhov <oleksii.dolhov@gmail.com>
…lease main diverged after v0.7.0 (Step-10 resync was skipped): the v0.7.0 squash plus 3 dependabot bumps (esbuild/form-data/vite — all superseded in dev) and the #1334 CI fix. dev is authoritative; this -s ours merge records main as a parent so the dev->main release PR is clean. The #1334 CI fix (missing from dev) is re-applied in the following commit.
- VERSION 0.7.0 -> 0.8.0 (read by /api/version + baked into the backend image) - Re-apply #1334: drop `-e ./src/cli` from tests/requirements-test.txt (it reds GitHub's Dependency-Graph check on every release) and install trinity_cli on demand from tests/setup-env.sh. The fix landed on main but was missing from dev. - Add docs/releases/0.8.0.md (110 issues across both trackers). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Release-prep: v0.8.0 (reconcile main + #1334 CI fix + version bump + notes)
| destination = config.fork_to_own.destination_repo | ||
| # Source-mode rows bypass the UNIQUE(repo, branch) index, so | ||
| # this is the guard against two agents auto-pushing the same | ||
| # destination main. (Re-checked after reservation below — this |
| # this is the guard against two agents auto-pushing the same | ||
| # destination main. (Re-checked after reservation below — this | ||
| # pre-check is check-then-act with the whole copy in between.) | ||
| bound_agents = db.get_git_config_agent_names_for_repo(destination) |
| ) | ||
| if rc != 0: | ||
| logger.error("fork-to-own: template clone failed for %s: %s", | ||
| template_repo, scrub_secret(out, read_pat)) |
| # the copy lands there and origin points at it). Also drives the | ||
| # featured card treatment in CreateAgentModal, with `tagline` as the | ||
| # card subtitle. | ||
| "fork_to_own": metadata.get("fork_to_own"), |
| # single-id resolve — `get_local_template()` / creation-by-id — still | ||
| # works for the test harness; `get_local_templates()` does the actual | ||
| # exclusion. (#1513) | ||
| "hidden": bool(data.get("hidden", False)), |
| ogg = await tts_service.synthesize_voice_note(spoken, cfg["voice_id"]) | ||
| if not ogg: | ||
| return False | ||
|
|
| # #1131: a denied docker.sock (e.g. wrong group_add GID on macOS Docker | ||
| # Desktop) raises here; swallowing it silently showed "No agents" in the | ||
| # UI with nothing in the logs. WARN so the failure is diagnosable — but | ||
| # throttled (see _SOCKET_WARN_THROTTLE_S) so a persistent denial on this |
…unit files (#1530) The base/head regression matrix (which only runs on PRs, not dev pushes) surfaced 16 order-dependent failures in test_fork_to_own / test_73_stats_cache / test_186_enumeration_uniformity — all new-on-dev files that pass in isolation but fail under full-suite ordering. Three distinct sys.modules-pollution vectors, all test-harness bugs (product code is intact); each fix only restores real modules: 1. test_voice_auth.py stubbed services.{docker_service,template_service, platform_audit_service} at module level and never restored them. The stubs are incomplete (no is_trinity_compatible / execute_command_in_container), so later files importing those symbols hit "ImportError: ... (unknown location)". Now snapshots + restores them after voice.py imports (mirrors test_voice_tools.py). 2. test_fork_to_own.py's crud_env teardown ran a manual "del sys.modules['services.agent_service.*']" AFTER patch.dict.stop() — which had already restored the pre-test snapshot. The delete left services.agent_service.stats absent, so test_73's module-level "stats_mod" reference went stale and its monkeypatch missed. Removed the redundant/harmful delete; patch.dict owns restore. 3. test_self_hosted_git_url.py's _reload_github_service() deletes+reimports services.github_service without restoring, leaking a fresh OwnerType enum; test_fork_to_own's owner-guard then compares OwnerType by identity across the two modules and silently skips ("DID NOT RAISE HTTPException"). Added an autouse fixture that snapshots/restores services.github_service* per test. Verified: full unit suite green under all three CI seeds (12345/67890/99999), 3646 passed / 15 skipped each (locally on py3.14; CI runs py3.11). Co-authored-by: Eugene Vyborov <eugene@beingluminous.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Release: v0.8.0
Cumulative platform release since v0.7.0 — 110 issues across the public (
abilityai/trinity) and private (abilityai/trinity-enterprise) trackers. Full notes:docs/releases/0.8.0.md.Highlights
Release mechanics
mainwas reconciled intodevin Release-prep: v0.8.0 (reconcile main + #1334 CI fix + version bump + notes) #1528 (-s ours+ fix(ci): stop the Dependency Graph check failing on every release #1334 CI fix re-applied), so this PR is conflict-free.0.8.0ondev.v0.8.0.trinity-enterprise) in-dev issues are closed manually post-merge (34 issues).Closes #73
Closes #186
Closes #187
Closes #267
Closes #292
Closes #388
Closes #506
Closes #654
Closes #679
Closes #792
Closes #846
Closes #894
Closes #903
Closes #905
Closes #918
Closes #919
Closes #945
Closes #946
Closes #954
Closes #959
Closes #1018
Closes #1084
Closes #1085
Closes #1103
Closes #1112
Closes #1131
Closes #1134
Closes #1155
Closes #1156
Closes #1157
Closes #1205
Closes #1266
Closes #1280
Closes #1281
Closes #1332
Closes #1342
Closes #1353
Closes #1360
Closes #1369
Closes #1376
Closes #1378
Closes #1400
Closes #1401
Closes #1406
Closes #1409
Closes #1410
Closes #1416
Closes #1418
Closes #1420
Closes #1422
Closes #1423
Closes #1424
Closes #1425
Closes #1426
Closes #1427
Closes #1432
Closes #1439
Closes #1441
Closes #1443
Closes #1444
Closes #1445
Closes #1446
Closes #1447
Closes #1450
Closes #1457
Closes #1461
Closes #1463
Closes #1464
Closes #1472
Closes #1478
Closes #1489
Closes #1491
Closes #1500
Closes #1501
Closes #1502
Closes #1513
Closes #1521