Skip to content

aep/types: AEPRecord has no first-class user_id / subject_id for cross-run behavior audit #20

Description

@telleroutlook

AEP has no first-class user_id / subject_id — cross-run behavior audit is awkward

Repo: packages/aep

Problem

AEP is per-run: run_id, trace_id, parent_trace_id, actions[], etc. Nothing on AEPRecord names the human user (or principal / subject) whose intent drove the run.

For a real agent platform serving many users concurrently, "audit all runs by user_lisi over the past week" is one of the most common questions. Today that requires borrowing run_context.delegation_chain[0] as a de-facto user id — which is a convention, not a contract.

Evidence

packages/aep/src/types.ts:

AEPRecordSchema (lines 98-123) — the top-level record has run_id, trace_id, parent_trace_id, repo_commit, runtime_version, model_provider, model_id, policy_bundle_digest, tool_manifest_digest, mcp_server_card_digest, input_refs, output_refs, capability_decisions, actions, verifier_results, budget_ledger, created_at_ms, run_context, signature. No user_id, no subject_id, no principal, no session_id.

RunContextSchema (lines 87-95):

export const RunContextSchema = z.object({
  agent_id: z.string().optional(),
  agent_version: z.string().optional(),
  subagent_id: z.string().optional(),
  delegation_chain: z.array(z.string()).default([]),
  environment_digest: z.string().optional(),
  dependency_lock_digest: z.string().optional(),
});

delegation_chain is meant for parent → child agent lineage, not user attribution. Yet in practice it's the only slot that can hold a user id.

CapabilityDecisionSchema (lines 4-11) has a subject: z.string() field — that's per-action, and its semantic scope isn't defined in the docs. Grep-checking docs/aep-contract.md for subject returns zero mentions.

Impact

  1. Downstream audit tools guess. Our project relies on record.run_context.delegation_chain[0] being the user id. If wasmagent's convention shifts (say to always prepend the agent that initiated the run), our behavior rules will silently start attributing actions to the wrong actor.
  2. Cross-run findings become second-class. open-agent-audit's rules operate per run. To get findings like "this user tried to bypass 5 denied tools in a week", we had to build a separate rule engine on top. If AEP had user_id, aggregation would be natural.
  3. CapabilityDecisionSchema.subject is potentially the right slot, but its semantics aren't documented, so nobody can rely on it.

Proposed fix

Two options, complementary:

Option 1 (small, additive): add a user_id?: string (or subject_id?: string) field to RunContextSchema, distinct from agent_id. Document that this is the human/principal whose intent drove the run, not the agent identity.

export const RunContextSchema = z.object({
  agent_id: z.string().optional(),
  agent_version: z.string().optional(),
  subagent_id: z.string().optional(),
  /** Human / principal id whose intent drove this run. */
  user_id: z.string().optional(),
  /** Optional session id — groups multiple runs by the same user in one product session. */
  session_id: z.string().optional(),
  delegation_chain: z.array(z.string()).default([]),
  environment_digest: z.string().optional(),
  dependency_lock_digest: z.string().optional(),
});

Option 2 (bigger): also document the CapabilityDecisionSchema.subject semantics — is it the user, the agent, or the caller of the current action? Right now the docs are silent.

Bonus: if the adapter (open-agent-audit/packages/adapters/src/aep-v0_2.ts) propagates user_id into every canonical event's actor field, downstream rule engines get user attribution for free.

We'd send a PR happily; want to align on which field naming (user_id vs subject_id vs principal_id) fits the wider WasmAgent ecosystem before we do.


Filed by: CATL Ariba Joule integration team. Behavior audit rules like "user X tried the same denied tool 5 times across 3 sessions" require a stable user attribution key across records; we borrow delegation_chain[0] today but would prefer a real field.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions