Skip to content

feat(ui): populate agent env from connection envMappings on grant#262

Merged
matoushavlena merged 1 commit intomainfrom
feat/connection-env-mappings
Apr 21, 2026
Merged

feat(ui): populate agent env from connection envMappings on grant#262
matoushavlena merged 1 commit intomainfrom
feat/connection-env-mappings

Conversation

@matoushavlena
Copy link
Copy Markdown
Contributor

@matoushavlena matoushavlena commented Apr 21, 2026

Summary

Brings GOOGLE_WORKSPACE_CLI_TOKEN back for the google-workspace agent without re-adding it to the Helm template. When a user grants a gmail, google-drive, or any other Google Workspace connection in the Configure Agent dialog, Humr copies the connection's declared envMappings into the agent's editable env list. The user can then edit or remove the entries like any custom env.

Requires OneCLI 0.0.20+ (kagenti/onecli#16, merged) which declares envMappings on the app registry and returns them on GET /api/connections. Humr image bumped accordingly in deploy/helm/humr/values.yaml.

Why

Two things the previous design tried got collapsed into one:

  • Who owns the provider → env knowledge? → OneCLI's app registry. Humr has zero provider-specific code paths.
  • Where do the envs end up? → On the agent (agentSpec.env), where they already live for every other per-agent env. No new injection pipeline, no reconciler changes, no connection-side metadata.

Result: the only Humr change is a UI interaction (populate on grant, auto-clean on untouched ungrant) + a pass-through of the envMappings field from OneCLI's response to the view.

Grant / ungrant behavior

  • Grant: append the app's declared envMappings to the editable env list. Skip entries whose name is already present (user-set wins).
  • Ungrant: remove entries the app contributed only if both conditions hold —
    • Untouched (name + value still match the declared mapping)
    • Not still needed by any other currently-granted app (shared envs like GOOGLE_WORKSPACE_CLI_TOKEN across Gmail + Drive stay)
  • Edited entries are preserved — an edited value signals user intent that toggling should not undo.

Logic is extracted as pure helpers in packages/ui/src/dialogs/connection-env-helpers.ts and covered by 10 vitest cases across all four grant/ungrant × touched/shared permutations plus the OneCLI-placeholder-change case.

What changes

  • api-server-api/connections/types.tsAppConnectionView.envMappings?: EnvMapping[] for UI consumption.
  • api-server/connections-service.ts — passes through OneCLI's joined envMappings verbatim. Tests use a fictional provider fixture (acme-app / ACME_TOKEN) — no Google-specific coupling.
  • ui/dialogs/connection-env-helpers.ts (new) — pure envsToAddOnGrant / envsAfterUngrant helpers.
  • ui/dialogs/edit-agent-secrets-dialog.tsxtoggleApp calls the helpers; functional updater reads fresh env state.
  • ui/__tests__/unit/connection-env-helpers.test.ts (new) — 10 unit tests.
  • ui/views/connections-view.tsx — shows env names on each connection row so users see what a grant will contribute.
  • deploy/helm/humr/values.yaml — OneCLI image 0.0.190.0.20.
  • docs/google-workspace/README.md — describes the grant-time flow.

Verification plan

  • mise run check passes (eslint + tsc + go build/vet + helm lint/render)
  • mise run test passes (controller + ui + agent-runtime; 34 ui tests including 10 new)
  • mise run api-server:test:unit passes (34 tests)

Manual end-to-end probe (requires OneCLI 0.0.20 running):

  1. Deploy: mise run cluster:install
  2. In OneCLI, connect Gmail or Google Drive (OAuth flow).
  3. Create or open a google-workspace agent.
  4. Click Configure → Connections tab → grant the Google connection.
  5. Switch to Environment tab → verify GOOGLE_WORKSPACE_CLI_TOKEN=humr:sentinel appears in the editable list.
  6. Save.
  7. mise run cluster:kubectl -- exec <agent-pod> -- env | grep GOOGLE_WORKSPACE → env present.
  8. From the agent pod: gws drive files list → returns real Google data.
  9. Reopen Configure, ungrant the connection, Save. Env entry should be removed automatically (because untouched).
  10. Re-grant + edit the value → ungrant → env entry stays (user edit preserved).

Existing google-workspace instances

Pods deployed before this change had GOOGLE_WORKSPACE_CLI_TOKEN injected by the helm template (removed in ADR-024). After upgrading to this release they will have no token env until the user re-opens Configure Agent and grants a Google connection (or manually sets the env). Release-note suggestion:

Google Workspace agent users: after upgrading, open each google-workspace agent → Configure → grant your Google Drive / Gmail connection. The env var injection moved from the template to grant-time and needs to be re-triggered once per agent.

Follow-up (out of scope here)

  • Anthropic secrets still use Humr-side ANTHROPIC_OAUTH_ENV_MAPPING / ANTHROPIC_API_KEY_ENV_MAPPING constants. Long-term we should move those declarations into OneCLI's secret-type registry (symmetric with this PR) so Humr owns zero env name knowledge.

@matoushavlena matoushavlena force-pushed the feat/connection-env-mappings branch 2 times, most recently from 1aaef63 to e1f64c8 Compare April 21, 2026 15:13
@matoushavlena matoushavlena changed the title feat: inject GOOGLE_WORKSPACE_CLI_TOKEN via connection envMappings feat(ui): populate agent env from connection envMappings on grant Apr 21, 2026
@matoushavlena matoushavlena force-pushed the feat/connection-env-mappings branch 2 times, most recently from e3dec5a to d2eff51 Compare April 21, 2026 15:43
When a user grants a connection (e.g. Google Drive) in the Configure
Agent dialog, any envMappings OneCLI declared for that app get appended
to the agent's editable env list. The user can then edit or remove the
entries like any other custom env var. On ungrant, entries that are
still untouched (name + value match the declared mapping) and not
required by any other still-granted app are removed automatically.

Requires OneCLI 0.0.20+ which adds `envMappings` as a first-class field
on the app registry and returns it on `GET /api/connections`
(kagenti/onecli#16).

Why this over storing envs on the connection itself: envs belong with
the agent (that's the K8s resource that consumes them), and users may
reasonably want to tweak the env name or remove a mapping for a
specific agent without losing the grant. Humr has zero provider
knowledge — the env-injection contract lives in OneCLI next to the
OAuth config for each app.

- api-server-api: `AppConnectionView.envMappings?` for UI consumption
- api-server: passes through OneCLI's joined envMappings verbatim on
  `connections.list`; tests use a fictional provider fixture so they
  don't couple to any specific provider
- ui: `EditAgentSecretsDialog.toggleApp` appends new app's envMappings
  to the agent env list on first grant (dedupe by env name — user-set
  wins); on ungrant removes entries only if they match the declared
  mapping and no other still-granted app declares the same envName
- ui: `ConnectorsView` displays env names on each connection row so
  users can see what a grant will contribute
- deploy: bump OneCLI image to 0.0.20
- docs: google-workspace README describes the new grant-time flow

Signed-off-by: Matous Havlena <havlenma@gmail.com>
@matoushavlena matoushavlena force-pushed the feat/connection-env-mappings branch from d2eff51 to 93ee0b4 Compare April 21, 2026 15:47
@matoushavlena matoushavlena merged commit 40d37be into main Apr 21, 2026
7 checks passed
@matoushavlena matoushavlena deleted the feat/connection-env-mappings branch April 21, 2026 15:48
matoushavlena added a commit that referenced this pull request Apr 21, 2026
Extends ADR-024's "env declared by credential owner" principle to OAuth
app connections. Three rationale points: users shouldn't have to know
env var names; Humr shouldn't decide env names for OneCLI-owned
providers; agent templates shouldn't bake connection envs since
connections are a runtime choice.

Shipped in kagenti/onecli#16 + #262.

Signed-off-by: Matous Havlena <havlenma@gmail.com>
matoushavlena added a commit that referenced this pull request Apr 21, 2026
Extends ADR-024's "env declared by credential owner" principle to OAuth
app connections. Three rationale points: users shouldn't have to know
env var names; Humr shouldn't decide env names for OneCLI-owned
providers; agent templates shouldn't bake connection envs since
connections are a runtime choice.

Shipped in kagenti/onecli#16 + #262.

Signed-off-by: Matous Havlena <havlenma@gmail.com>
matoushavlena added a commit that referenced this pull request Apr 21, 2026
Extends ADR-024's "env declared by credential owner" principle to OAuth
app connections. Three rationale points: users shouldn't have to know
env var names; Humr shouldn't decide env names for OneCLI-owned
providers; agent templates shouldn't bake connection envs since
connections are a runtime choice.

Shipped in kagenti/onecli#16 + #262.

Signed-off-by: Matous Havlena <havlenma@gmail.com>
matoushavlena added a commit that referenced this pull request Apr 21, 2026
Slimmed the ADR from 143 lines to 43 — kept the decision, dropped
implementation detail (zod schemas, Prisma columns, key files,
verification blocks) that belongs in commit messages and PRs.

Unified the secret + connection paths under one principle: "the entity
that owns the credential declares which env vars it needs." The two
paths differ in storage (secret metadata vs OneCLI app registry) and
flow (controller-merged vs UI-populated) but share the rationale.

Captures the four guiding constraints: users shouldn't know env names;
Humr shouldn't decide env names for OneCLI-owned providers; templates
shouldn't bake connection envs; power users can still tweak per-agent.

Tracked in kagenti/onecli#9 (secrets), kagenti/onecli#16 +
#262 (connections).

Signed-off-by: Matous Havlena <havlenma@gmail.com>
matoushavlena added a commit that referenced this pull request Apr 21, 2026
Slimmed the ADR from 143 lines to 43 — kept the decision, dropped
implementation detail (zod schemas, Prisma columns, key files,
verification blocks) that belongs in commit messages and PRs.

Unified the secret + connection paths under one principle: "the entity
that owns the credential declares which env vars it needs." The two
paths differ in storage (secret metadata vs OneCLI app registry) and
flow (controller-merged vs UI-populated) but share the rationale.

Captures the four guiding constraints: users shouldn't know env names;
Humr shouldn't decide env names for OneCLI-owned providers; templates
shouldn't bake connection envs; power users can still tweak per-agent.

Tracked in kagenti/onecli#9 (secrets), kagenti/onecli#16 +
#262 (connections).

Signed-off-by: Matous Havlena <havlenma@gmail.com>
matoushavlena added a commit that referenced this pull request Apr 21, 2026
…ime (#269)

* fix(ui): populate agent env from connection envMappings at creation time

PR #262 wired envMappings to agent env only inside the Configure Agent
dialog (EditAgentSecretsDialog). The Add Agent flow — AddAgentDialog →
createAgent → agents.create tRPC — never passed env, and the create
router didn't accept it. Creating a new agent with Gmail (or any app
declaring envMappings) enabled produced empty env in spec and pod.

- api-server-api: `agents.create` input and `CreateAgentInput` accept
  optional `env`
- api-server: `assembleSpecFromTemplate`/`assembleSpecFromImage` merge
  create-time env into the spec via a new `mergeEnv` helper that
  preserves protected names (PORT) from template/defaults and lets
  extras override non-protected collisions
- ui: `createAgent` store action forwards `env`; `AddAgentDialog.submit`
  derives env from selected apps' envMappings by reducing
  `envsToAddOnGrant` across granted apps (dedupes shared envNames)

Closes the gap so the grant-time populate behaves identically whether
the user grants during create or later in Configure.

Signed-off-by: Matous Havlena <havlenma@gmail.com>

* refactor(api-server): unify create/update env handling via preserveProtectedEnvs

Drop the create-path `mergeEnv` helper and revert spec-assembly. The
service now appends caller extras via the existing `preserveProtectedEnvs`
used by update — one protected-env helper covers both paths. PORT is
always sourced from template/defaults regardless of what the caller sends.

Signed-off-by: Matous Havlena <havlenma@gmail.com>

---------

Signed-off-by: Matous Havlena <havlenma@gmail.com>
matoushavlena added a commit that referenced this pull request Apr 22, 2026
Slimmed the ADR from 143 lines to 43 — kept the decision, dropped
implementation detail (zod schemas, Prisma columns, key files,
verification blocks) that belongs in commit messages and PRs.

Unified the secret + connection paths under one principle: "the entity
that owns the credential declares which env vars it needs." The two
paths differ in storage (secret metadata vs OneCLI app registry) and
flow (controller-merged vs UI-populated) but share the rationale.

Captures the four guiding constraints: users shouldn't know env names;
Humr shouldn't decide env names for OneCLI-owned providers; templates
shouldn't bake connection envs; power users can still tweak per-agent.

Tracked in kagenti/onecli#9 (secrets), kagenti/onecli#16 +
#262 (connections).

Signed-off-by: Matous Havlena <havlenma@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants