Skip to content

Sync upstream paradigmxyz/centaur - #54

Open
darkmatter[bot] wants to merge 198 commits into
mainfrom
sync/upstream-main
Open

Sync upstream paradigmxyz/centaur#54
darkmatter[bot] wants to merge 198 commits into
mainfrom
sync/upstream-main

Conversation

@darkmatter

@darkmatter darkmatter Bot commented Aug 26, 2026

Copy link
Copy Markdown

Upstream sync candidate — PR #54 is the long-lived sync PR; do not close or recreate.

  • Upstream tip: f5ef8b8fcbdcea7b9d9690ffaa96d9090f995941
  • Product base: c9fadb1dec5439c80325060970a1093134a9e719
  • Candidate head: 4519575e44c26f4b67161278a8604430dd57533b

MERGE POLICY: Only a human may merge this PR. Merge-commit ancestry is required (--no-ff); squash/rebase merges are forbidden. First parent must be product main, second parent the upstream tip.

This candidate contains both current product main and the current upstream tip. The repair stage must validate or rebuild this merge before it goes ready-for-review.

Matthew Slipper (mslipper) and others added 30 commits July 31, 2026 11:28
* fix(workflows): shut down Python host cleanly

* fix(workflows): accept large host messages
* feat: scope company context reader user sources

* fix: retain Google subject context authorization

* test: strengthen company context reader RLS guards
* fix(console): restore inline markdown links

* fix(console): parse markdown placeholder indexes

* fix(console): preserve restored markdown fragments

* test(console): generalize markdown link regression
* feat(console): promote principal identity fields

* refactor(console): normalize principal identifiers

* revert(console): remove normalized principal identifiers

* fix(console): infer principal kinds from foreign IDs

* fix(console): require scoped Slack DM identifiers

* refactor(console): keep principal identity out of labels

* refactor(console): simplify principal label aliases

* fix(console): validate principal identity fields

* fix(console): support Slack enterprise identity scopes

* test: cover principal identity reconciliation

* fix: preserve principal identity compatibility

* fix: narrow principal kind backfill

* fix: validate reconciled Slack identities

* refactor: centralize principal identity promotion

* fix: validate MCP Slack identities

* fix: show principal identity labels in console
* feat: add default role assignments

* fix: preserve default roles when settings omit them
* feat(console): promote console user identity fields

* fix(console): reference console users by database id

* fix(console): allow stale console user references

* refactor(console): centralize principal identity labels
* fix(chart): require console

* refactor(api-rs): always enable iron-proxy

* fix(chart): ignore removed enable values
Co-authored-by: Perry Dime <260989497+svc-paradigm@users.noreply.github.com>
Selects the awscliv2 package matching the image's dpkg architecture
(x86_64/aarch64), following the same pattern already used for kubectl
and Nushell in this Dockerfile.
Co-authored-by: Perry Dime <260989497+svc-paradigm@users.noreply.github.com>
* fix(docsend): encode browserbase session metadata

* refactor(docsend): use base64 session metadata

* refactor(docsend): require encoded session metadata

* refactor(docsend): simplify base64 decoding

---------

Co-authored-by: Perry Dime <260989497+svc-paradigm@users.noreply.github.com>
* feat(slackbotv2): decouple response metadata from Console

* feat(slackbotv2): split response metadata controls

* chore(chart): bump release to 0.1.110

* feat(slackbotv2): add response metadata modes

---------

Co-authored-by: Liam Horne <liam@lihorne.com>
_serialize_message builds a fixed dict from the Slack payload and drops
everything it doesn't name, including the reactions array. Every read path
funnels through it (get_channel_history_page, get_thread_replies_page, and
the search fallback fetch), so no caller of the serialized shape can see
reaction signal at all.

That's a real blind spot in channels where people answer by reacting rather
than replying. A message with 12 check-marks and no replies serializes to
reply_count 0, which reads as "nobody responded" when the opposite is true.
An agent working off that shape either reports the reply count as the roster
or has to admit it can't answer.

The data is already on the wire and needs no new OAuth scope. reactions:read
gates the reactions.* methods, while the reactions array on a
conversations.history payload rides along with channels:history, which this
tool already requires. feedback.py:372 has been reading them straight off the
raw responses this way for its thumbsup/thumbsdown signal. Only the
serializer was throwing them away.

Consistent across both read paths: the API server proxy hands back an
untyped serde_json::Value, so reactions survive it and the proxy and direct
fetches agree.

Defaults to [] like reply_users so callers can index without a guard. Slack
caps the per-reaction users array, so count can exceed len(users); anyone
needing a complete reactor list on a heavily reacted message still wants
reactions.get and the reactions:read scope it requires. The comment says so
at the call site.

Co-authored-by: Aadharsh Pannirselvam <19518661+oddharsh@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ebhooks (paradigmxyz#1248)

* feat(githubbot): produce durable workflow events from CI and review webhooks

Add a workflow-event producer module, independent of the owned-PR manager:
lifecycle webhooks are translated into curated durable events on api-rs
(POST /api/workflows/events) that workflows suspend on via
ctx.wait_for_event. Emission runs before any owned-PR gating, because
workflow waiters are not bot-owned PRs.

- ci-completed (<owner>/<repo>:<head_sha>, payload {failed, failing})
  fires once every check for the sha has settled. The settled evaluation
  reads GitHub's GraphQL statusCheckRollup — the same aggregate gh pr
  checks uses, covering check runs and commit statuses including EXPECTED
  — because fine-grained PATs get an ungrantable 403 on the REST
  check-runs list. An unreadable rollup is unknown, never settled, and a
  settled-green rollup is confirmed by one delayed re-read before
  emission: a push can read SUCCESS moments before the real suite
  registers.
- review-submitted (<owner>/<repo>:pr-<n>:<head_sha>:<reviewer>, payload
  {review_id, state}) fires on every submitted review. Author-scoped
  correlations give each reviewer an independent row, so a waiter keys on
  exactly the author it cares about with no emission-side config.

Durable events are immutable per correlation (first write wins), which
forces two curation rules documented in the module header: an event must
be semantically complete when emitted (the settled gate), and anything a
waiter filters on belongs in the correlation. Correlations are computable
from data the waiter already has and are lowercased so case drift between
a PR URL slug and repository.full_name can never miss.

The manager shares the settled evaluation so an owned PR is not evaluated
twice per CI event. Off by default (githubbot.workflowEvents /
GITHUBBOT_WORKFLOW_EVENTS); the api-rs URL and service token are already
wired for the session API.

* fix(githubbot): harden workflow event delivery

* refactor(githubbot): simplify workflow event code
…e in the ruby-dependencies group (paradigmxyz#1259)

chore(deps): bump solid_queue

Bumps the ruby-dependencies group in /services/console with 1 update: [solid_queue](https://github.com/rails/solid_queue).


Updates `solid_queue` from 1.5.0 to 1.6.0
- [Release notes](https://github.com/rails/solid_queue/releases)
- [Commits](rails/solid_queue@v1.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: solid_queue
  dependency-version: 1.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: ruby-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat: add OpenAI workflow dependency

* fix: require OpenAI SDK 2.53
@darkmatter
darkmatter Bot force-pushed the sync/upstream-main branch from d38e916 to 8978397 Compare August 27, 2026 22:26
@darkmatter
darkmatter Bot marked this pull request as ready for review August 27, 2026 22:27
@darkmatter
darkmatter Bot force-pushed the sync/upstream-main branch from 8978397 to 2150df7 Compare August 27, 2026 22:35
@darkmatter
darkmatter Bot marked this pull request as draft August 28, 2026 13:01
@darkmatter
darkmatter Bot marked this pull request as ready for review August 28, 2026 13:02
Iron-proxy pods were created with restartPolicy=Never, so any container
crash left the pod permanently Failed while its sandbox kept running with
zero egress: Codex gets connection refused to api.openai.com, in-sandbox
tools fail, and the turn dies. Nothing repairs the proxy until the next
session execute calls assign_proxy_principal and recreates it, so every
in-flight turn on that sandbox is lost.

This went from latent to load-bearing on prd-centaur-na when
tempoxyz/prd-centaur-infra#669 added a 512Mi memory limit: iron-proxy
buffers bodies while proxying, large transfers burst past the limit in
seconds, and the resulting OOM kills produced 40+ mid-turn 'stream
disconnected before completion' failures since 2026-08-27.

OnFailure makes the kubelet restart the container in place: same pod IP,
the per-sandbox Service keeps routing, and the proxy re-syncs its
principal config from iron-control on startup, so an OOM becomes a
seconds-long blip instead of a dead session. pod_running() requires the
Ready condition, so ensure/assign paths still treat a crash-looping proxy
as unusable, and wait_until_proxy_running now bounds a proxy that never
comes up by ready_timeout instead of failing on first crash.

Amp-Thread-ID: https://ampcode.com/threads/T-01a048d9-a0bb-72f0-b6cd-4769da726fdc

Co-authored-by: Amp <amp@ampcode.com>
@darkmatter
darkmatter Bot force-pushed the sync/upstream-main branch from 2150df7 to dc87a81 Compare August 28, 2026 19:01
@darkmatter
darkmatter Bot marked this pull request as draft August 28, 2026 19:01
@darkmatter
darkmatter Bot force-pushed the sync/upstream-main branch 4 times, most recently from 9dd553d to dc87a81 Compare August 28, 2026 20:02
@darkmatter
darkmatter Bot marked this pull request as ready for review August 28, 2026 20:22
@darkmatter
darkmatter Bot force-pushed the sync/upstream-main branch from 9dd553d to dc87a81 Compare August 29, 2026 21:01
@darkmatter
darkmatter Bot marked this pull request as draft August 29, 2026 21:01
@darkmatter darkmatter Bot changed the title chore(upstream): sync paradigmxyz/centaur:main (d0ccee44) Sync upstream paradigmxyz/centaur main (dc87a81) Aug 29, 2026
@darkmatter darkmatter Bot changed the title Sync upstream paradigmxyz/centaur main (dc87a81) chore(upstream): sync product main with paradigmxyz/centaur@dc87a81 Aug 29, 2026
@darkmatter darkmatter Bot changed the title chore(upstream): sync product main with paradigmxyz/centaur@dc87a81 Sync upstream paradigmxyz/centaur main (dc87a81) Aug 29, 2026
@darkmatter
darkmatter Bot marked this pull request as ready for review August 29, 2026 21:35
@darkmatter
darkmatter Bot force-pushed the sync/upstream-main branch from 9dd553d to dc87a81 Compare August 29, 2026 22:01
@darkmatter
darkmatter Bot marked this pull request as draft August 29, 2026 22:01
…z#1496)

* feat: bind the console user's principal on console thread turns

Console sessions register an anonymous per-thread principal, so a user's
connected OAuth credentials (e.g. GitHub) never reach console-driven turns:
reconciliation matches identity-scoped credentials to principals, and the
thread principal carries no user identity. The proxy falls back to shared
role-granted tokens and PRs open as the bot.

The console now provisions the authenticated user's console-user principal
on each execute and passes its foreign ID as requester_principal_foreign_id
in the execute metadata. api-rs resolves it fetch-only for console: thread
keys (a namespace only the console service may write) and binds it as the
turn's requester principal, so the proxy serves the RFC 0005 union: the
thread principal's grants plus the requester's always-available OAuth direct
grants. Shared threads stay safe — a reply binds the replier's principal,
never the creator's. The provisioner no longer rewrites an unchanged
principal on repeat calls.

The RFC 0005 availability gate is unchanged: a credential joins console
turns only when an admin marked its OAuth app always-available.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* feat: bind the comment author's principal on GitHub turns

GitHub-triggered turns ran with only the anonymous per-thread principal, so
a commenter's connected GitHub OAuth credential never applied: githubbot
already forwards the webhook-verified sender (`user_id`, the numeric GitHub
id, and `user_name`) in every execute's metadata, but api-rs resolved a
requester only for Slack.

api-rs now derives and upserts a per-user `github-user-<id>` principal for
`github:` thread keys, labeled `github_subject: <id>`, and binds it as the
turn's requester principal. Reconciliation gains GitHub as a subject-label
provider (`Principal::KINDS` learns `github_user`), so the credential owner
is matched by GitHub user id — the only workable anchor, since the consent
flow collects no email scope. The proxy then serves the RFC 0005 union:
thread principal's grants plus the requester's always-available OAuth direct
grants, so pushes and PRs authenticate as the commenter.

A commenter can only bind their own identity: the sender id is authentic
from the signature-verified webhook, turns only run for author associations
the deployment allowlisted, and the hoisted credential is always the
commenter's own.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* refactor: tighten console requester comments

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* refactor: plan-based requester resolution and review cleanup

Address review findings on the requester binding:

- Collapse register_requester's per-source branches into a typed
  RequesterPlan (FetchExisting for console-provisioned principals,
  UpsertDerived for api-rs-owned Slack/GitHub principals) produced by one
  dispatch function, so a new source is one arm instead of another branch
  in the registrar.
- The test stub now records request bodies, and the GitHub upsert test
  asserts the serialized kind/name/github_subject payload, not just the
  path.
- RFC 0005 §4 rewritten around the single precedence rule (provider-native
  subject first, owner-identity/email fallback) so it no longer contradicts
  the GitHub subject path; Security Considerations lists the webhook and
  console trust anchors.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: resolve GitHub requesters on work sessions

---------

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Matthew Slipper <me@matthewslipper.com>
Co-authored-by: Liam Horne <1933029+snario@users.noreply.github.com>
* feat: add experimental memory generation workflow

* fix: resolve memory workflow CI failures

* fix: align memory generation model with evals

* fix: prevent memory generation pipeline wedges

* fix: preserve cursor on transient memory failures

* fix: reject all Slack group DMs from memory scope

* feat: add self-draining memory backfill
* fix(codex): set default reasoning to medium

* test: update Slack reasoning metadata expectations

---------

Co-authored-by: Perry Dime <260989497+svc-paradigm@users.noreply.github.com>
Co-authored-by: Matthew Slipper <me@matthewslipper.com>
* feat: add Centaur agent plugin registry

* test: lock centaur plugin identity
* fix: retry transient console sync failures

* test: fix Granola sync retry stub

---------

Co-authored-by: Perry Dime <260989497+svc-paradigm@users.noreply.github.com>
Merge upstream tip f5ef8b8 into product
base c9fadb1.

Conflicts resolved using the prior validated merge 87af8b8 (base c9fadb1
+ fd3c260) as resolution oracle for all paths upstream did not touch in
fd3c260..f5ef8b8; paths upstream changed after fd3c260 take the
upstream tip version. Fork invariants preserved: telemetry opentelemetry-
proto/prost deps, migration renumbering (0057/0058), sandbox
api_server_enabled capability, workspace-diff API, OMP harness support.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.