From 8daef658759b0e524bd4295da43734c1d98fadf9 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 24 Sep 2026 19:23:27 +0000 Subject: [PATCH] feat(agent-relay-claude-code): declare and expose the agent_relay_client_platform parameter --- .../modules/agent-relay-claude-code/README.md | 9 +++++- .../modules/agent-relay-claude-code/main.tf | 30 +++++++++++++++++++ .../agent-relay-claude-code/main.tftest.hcl | 7 +++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/registry/coder/modules/agent-relay-claude-code/README.md b/registry/coder/modules/agent-relay-claude-code/README.md index 535ea2af0..75532c810 100644 --- a/registry/coder/modules/agent-relay-claude-code/README.md +++ b/registry/coder/modules/agent-relay-claude-code/README.md @@ -16,7 +16,7 @@ on each build and runs the Claude Code self-hosted runner. ```tf module "claude_code_runner" { source = "registry.coder.com/coder/agent-relay-claude-code/coder" - version = "0.1.1" + version = "0.2.0" agent_id = coder_agent.main.id # Downloads the Claude Code CLI at start when it is not in the image. Bake @@ -55,6 +55,13 @@ startup and refuses to serve a pool that does not satisfy it. Every parameter renders disabled with a "Set by Agent Relay on dispatch" placeholder; the credential is masked. +`agent_relay_client_platform` is attribution rather than contract: the relay +stamps the surface the session was created from (`web_claude_ai`, +`claude_in_slack`, `desktop_app`, ...) and the module exposes it to the +workspace as `AGENT_RELAY_CLIENT_PLATFORM` and as the `client_platform` +output, so a template can vary its setup by origin. A relay that predates the +parameter leaves it empty. + ## Scripts and logs The module runs two steps through [coder-utils](https://registry.coder.com/modules/coder/coder-utils): diff --git a/registry/coder/modules/agent-relay-claude-code/main.tf b/registry/coder/modules/agent-relay-claude-code/main.tf index 7e9f86d01..55d523441 100644 --- a/registry/coder/modules/agent-relay-claude-code/main.tf +++ b/registry/coder/modules/agent-relay-claude-code/main.tf @@ -12,6 +12,9 @@ # persistent state: coderd only stores parameter values the # template declares, and Agent Relay's dedupe and reconciliation query # workspaces with `param:` search filters on them. +# - agent_relay_client_platform is persistent attribution: the surface +# that created the session. Stamped on every build; relays that +# predate it simply never set it. # - agent_relay_credential, agent_relay_claude_code_lock_to_account, # and agent_relay_attempt are ephemeral runner inputs, reset # between builds. An ephemeral value is still recorded on the build @@ -138,6 +141,20 @@ data "coder_parameter" "agent_relay_pool" { }) } +data "coder_parameter" "agent_relay_client_platform" { + name = "agent_relay_client_platform" + display_name = "Claude Code client platform" + description = "Surface the session was created from: web_claude_ai, desktop_app, ios, android, claude_code_cli, or claude_in_slack. Agent Relay sets this when it dispatches the workspace; a human never fills it in. Exposed to the workspace as AGENT_RELAY_CLIENT_PLATFORM so a template can vary its setup by origin." + type = "string" + mutable = true + default = "" + order = 1006 + styling = jsonencode({ + disabled = true + placeholder = "Set by Agent Relay on dispatch" + }) +} + data "coder_parameter" "agent_relay_credential" { name = "agent_relay_credential" display_name = "Agent Relay credential" @@ -198,6 +215,14 @@ resource "coder_env" "agent_relay_claude_code_lock_to_account" { value = data.coder_parameter.agent_relay_claude_code_lock_to_account.value } +# Agent Relay's own variable, not the CLI's: attribution for scripts and +# tooling inside the workspace. +resource "coder_env" "agent_relay_client_platform" { + agent_id = var.agent_id + name = "AGENT_RELAY_CLIENT_PLATFORM" + value = data.coder_parameter.agent_relay_client_platform.value +} + locals { # coder-utils requires this exact layout. Scripts land in scripts/ and # their output in logs/; the runner state and log default to the same @@ -253,3 +278,8 @@ output "dispatched" { description = "Whether this workspace was spawned by Agent Relay (credential set) or manually (empty)." value = data.coder_parameter.agent_relay_credential.value != "" } + +output "client_platform" { + description = "Surface the session was created from (web_claude_ai, claude_in_slack, ...), or empty when the relay predates the parameter or the workspace was created manually." + value = data.coder_parameter.agent_relay_client_platform.value +} diff --git a/registry/coder/modules/agent-relay-claude-code/main.tftest.hcl b/registry/coder/modules/agent-relay-claude-code/main.tftest.hcl index a80e84c9d..8c725cbd3 100644 --- a/registry/coder/modules/agent-relay-claude-code/main.tftest.hcl +++ b/registry/coder/modules/agent-relay-claude-code/main.tftest.hcl @@ -40,6 +40,11 @@ run "parameter_contract" { error_message = "attempt parameter name is part of the Agent Relay contract" } + assert { + condition = data.coder_parameter.agent_relay_client_platform.name == "agent_relay_client_platform" + error_message = "client platform parameter name is what Agent Relay stamps" + } + # The relay reads these back off a build long after dispatch, so they # must not be ephemeral; the rest must be, so a manual build never # inherits a stale credential, account lock, or attempt. @@ -48,6 +53,7 @@ run "parameter_contract" { data.coder_parameter.agent_relay_session_id.ephemeral == false, data.coder_parameter.agent_relay_delivery_id.ephemeral == false, data.coder_parameter.agent_relay_pool.ephemeral == false, + data.coder_parameter.agent_relay_client_platform.ephemeral == false, data.coder_parameter.agent_relay_credential.ephemeral == true, data.coder_parameter.agent_relay_claude_code_lock_to_account.ephemeral == true, data.coder_parameter.agent_relay_attempt.ephemeral == true, @@ -63,6 +69,7 @@ run "parameter_contract" { data.coder_parameter.agent_relay_session_id.styling, data.coder_parameter.agent_relay_delivery_id.styling, data.coder_parameter.agent_relay_pool.styling, + data.coder_parameter.agent_relay_client_platform.styling, data.coder_parameter.agent_relay_credential.styling, data.coder_parameter.agent_relay_claude_code_lock_to_account.styling, data.coder_parameter.agent_relay_attempt.styling,