Page actions

React Microsoft Agent Framework expense preview

Review a fictional expense with a Microsoft Agent Framework backend. React displays the proposal and result; AG-UI carries the run events and the explicit approval response.

The native React APIs on this page are experimental, private source-build packages. Run the repository example to use them; they are not an npm installation recipe.

Preserve the pending decision identity

The native session observes the backend’s interrupt, and the application supplies callbacks only for the current expense decision. It then checks the resumed run and literal tool result before reporting the outcome. The state panel is an observation of the backend; it cannot substitute for the pending interrupt when authorizing a command.

The React conversation walkthrough connects this capability to requests, tools, UI state, and saved history.

Request and review an expense

Choose Run, then Team dinner for 4 to fill an editable message. Only Send starts a request or creates a native Session and thread. The read-only predictive draft is separate from canonical approval fields. Approve and Decline become available only for a current correlated native approval for submit_expense. A local draft, assistant prose, or compatibility state cannot authorize a decision. Decline resumes with approved: false; it is distinct from Stop.

app.tsx
// eslint-disable-next-line @nx/enforce-module-boundaries -- The isolated consumer copies this internal example layout.
import { ExampleFooter, ExampleLayout } from '../../../../shared/react-example';
import { useEffect, useLayoutEffect, useState } from 'react';
import { projectTextTranscript } from '@threadplane/ag-ui';
import { useAgent } from '@threadplane/react';
import {
  ApprovalCard,
  ChatInput,
  TextTranscript,
} from '@threadplane/react/chat';
import { createConnectedApplication, type mafConnection } from './connection';
import type { Expense } from './approval-policy';
 
export const EXPENSE_PRESET =
  'This is a fictional demo expense. Please look up the meals expense policy, then submit this expense for approval: Blue Finch Bistro, category meals, $220.00, memo: Team dinner for 4 attendees; receipt attached.';
 
function ExpenseDetails({
  expense,
}: {
  readonly expense: Readonly<Partial<Expense>>;
}) {
  return (
    <dl className="expense-details">
      <dt>Vendor</dt>
      <dd>{expense.vendor ?? 'Not provided'}</dd>
      <dt>Category</dt>
      <dd>{expense.category ?? 'Not provided'}</dd>
      <dt>Amount (USD)</dt>
      <dd>
        {expense.amount_usd === undefined
          ? 'Not provided'
          : `$${expense.amount_usd.toFixed(2)}`}
      </dd>
      <dt>Memo</dt>
      <dd>{expense.memo ?? 'Not provided'}</dd>
    </dl>
  );
}
 
export function MafDemo({
  connection,
  onReady,
}: {
  readonly connection: ReturnType<typeof mafConnection>;
  readonly onReady: () => void;
}) {
  // Applications are lazy: construction allocates neither a thread nor a Session.
  // The stable owner survives StrictMode effect rehearsal; disposed applications do not.
  type Application = ReturnType<typeof createConnectedApplication>;
  const [owner] = useState<{
    current?: Application;
    connection: typeof connection;
  }>(() => ({ connection }));
  const [application, setApplication] = useState<Application | null>(null);
  useLayoutEffect(() => {
    if (!owner.current || owner.current.getSnapshot().status === 'disposed')
      owner.current = createConnectedApplication(owner.connection);
    const acquired = owner.current;
    setApplication(acquired);
    return () => {
      void acquired.dispose().catch(() => undefined);
    };
  }, [owner]);
  return application ? (
    <MafPresentation
      application={application}
      label={owner.connection.label}
      onReady={onReady}
    />
  ) : (
    <p role="status">Starting expense preview…</p>
  );
}
 
function MafPresentation({
  application,
  label,
  onReady,
}: {
  readonly application: ReturnType<typeof createConnectedApplication>;
  readonly label: ReturnType<typeof mafConnection>['label'];
  readonly onReady: () => void;
}) {
  const [draft, setDraft] = useState('');
  const snapshot = useAgent(application);
  const generation = snapshot.generation;
  const approval = snapshot.approval;
  useEffect(() => {
    if (application.getSnapshot().status !== 'disposed') onReady();
  }, [application, onReady]);
  useEffect(() => {
    setDraft('');
  }, [generation]);
  const status = {
    ready: 'Ready.',
    running: 'Working on the expense…',
    pending: 'Awaiting your decision.',
    approved: 'Fictional expense approved.',
    declined: 'Expense declined.',
    unconfirmed: 'Expense unconfirmed. Start a new conversation.',
    'cleanup-failed':
      'Conversation cleanup failed. Reload the preview to continue.',
    replacing: 'Starting new conversation…',
    disposed: 'Preview closed.',
  }[snapshot.status];
  return (
    <ExampleLayout className="maf-demo">
      <header>
        <div>
          <h1>Microsoft Agent Framework expenses</h1>
        </div>
        <button
          type="button"
          disabled={
            snapshot.status === 'replacing' ||
            snapshot.status === 'disposed' ||
            snapshot.status === 'cleanup-failed'
          }
          onClick={() => {
            void application.newConversation();
          }}
        >
          New conversation
        </button>
      </header>
      <p>
        Review a fictional expense, then approve or decline it. No real expense
        is submitted.
      </p>
      <p className="preview-note">Source-build preview · {label}</p>
      <button
        type="button"
        className="preset"
        disabled={!snapshot.canSend}
        onClick={() => setDraft(EXPENSE_PRESET)}
      >
        Team dinner for 4
      </button>
      <TextTranscript
        messages={projectTextTranscript(snapshot.rows)}
        label="Conversation"
      />
      {snapshot.draft && (
        <section
          aria-label="Predictive expense draft"
          className="predictive-draft"
        >
          <h2>Predictive expense draft</h2>
          <p>Read-only preview. Decisions use the separate expense approval.</p>
          <ExpenseDetails expense={snapshot.draft} />
        </section>
      )}
      {approval && (
        <ApprovalCard
          title="Expense approval"
          disabled={snapshot.busy || !snapshot.canRespond}
          actions={[
            {
              id: 'approve',
              label: 'Approve',
              onSelect: () => {
                void application.decide(approval, 'approve', generation);
              },
            },
            {
              id: 'decline',
              label: 'Decline',
              onSelect: () => {
                void application.decide(approval, 'decline', generation);
              },
            },
          ]}
        >
          <ExpenseDetails expense={approval.expense} />
          <p>This decision applies only to the fictional expense shown here.</p>
        </ApprovalCard>
      )}
      {snapshot.result !== null && (
        <section
          aria-label="Observed backend result"
          className="backend-result"
        >
          <h2>Observed backend result</h2>
          <p>The expense tool returned this exact text:</p>
          <pre>{snapshot.result}</pre>
        </section>
      )}
      <ExampleFooter>
        {snapshot.error && <p role="alert">{snapshot.error}</p>}
        <section aria-label="Compose message">
          <ChatInput
            label="Message"
            placeholder="Ask about a fictional expense…"
            value={draft}
            onValueChange={setDraft}
            disabled={!snapshot.canSend}
            busy={snapshot.busy}
            onStop={
              snapshot.busy && snapshot.status !== 'replacing'
                ? () => {
                    void application.stop();
                  }
                : undefined
            }
            onSubmit={(text) => {
              void application.submit(text, generation);
              const current = application.getSnapshot();
              return (
                current.generation === generation &&
                current.busy &&
                current.status === 'running'
              );
            }}
          />
        </section>
        <p role="status">{status}</p>
      </ExampleFooter>
    </ExampleLayout>
  );
}
approval-policy.ts
import type { Session } from '@threadplane/ag-ui';
export type NativeSnapshot = ReturnType<Session['getSnapshot']>;
type Transcript = NativeSnapshot['transcript'];
export interface Expense {
  readonly vendor: string;
  readonly category: string;
  readonly amount_usd: number;
  readonly memo: string;
}
export interface OwnedTurn {
  readonly threadId: string;
  readonly runId: string;
  readonly human: Transcript[number];
  readonly prefix: Transcript;
}
export interface ExpenseApproval {
  readonly threadId: string;
  readonly sourceRunId: string;
  /** Original opaque native occurrence; never reconstruct from observation data. */
  readonly pause: Parameters<Session['resume']>[0];
  readonly interruptId: string;
  readonly toolCallId: string;
  readonly expense: Expense;
  readonly transcript: Transcript;
  readonly observation: NativeSnapshot;
}
export function record(value: unknown): Record<string, unknown> | undefined {
  return value !== null && typeof value === 'object' && !Array.isArray(value)
    ? (value as Record<string, unknown>)
    : undefined;
}
/** Reject accessors/cycles and bound both traversal and retained text. Recheck after every proxy trap. */
export function ownData(value: unknown, valid = () => true): unknown {
  let nodes = 100000,
    characters = 1000000;
  const seen = new Set<object>();
  function copy(input: unknown, depth: number): unknown {
    if (!valid() || --nodes < 0 || depth > 32)
      throw Error('Revoked or oversized observation');
    if (typeof input === 'string') {
      characters -= input.length;
      if (characters < 0) throw Error('Oversized text');
      return input;
    }
    if (input === null || input === undefined || typeof input === 'boolean')
      return input;
    if (typeof input === 'number' && Number.isFinite(input)) return input;
    if (typeof input !== 'object' || seen.has(input))
      throw Error('Not plain data');
    const array = Array.isArray(input),
      prototype = Object.getPrototypeOf(input);
    if (
      !valid() ||
      (!array && prototype !== Object.prototype && prototype !== null)
    )
      throw Error('Not plain data');
    let arrayLength: number | undefined;
    if (array) {
      const descriptor = Object.getOwnPropertyDescriptor(input, 'length');
      if (
        !valid() ||
        !descriptor ||
        !('value' in descriptor) ||
        !Number.isSafeInteger(descriptor.value) ||
        descriptor.value < 0 ||
        descriptor.value > nodes
      )
        throw Error('Revoked or oversized array');
      arrayLength = descriptor.value as number;
    }
    seen.add(input);
    const output: Record<string, unknown> | unknown[] = array ? [] : {};
    const keys = Reflect.ownKeys(input);
    if (!valid() || keys.length > nodes)
      throw Error('Revoked or oversized keys');
    if (arrayLength !== undefined) {
      // Reject sparse arrays instead of retaining uncharged logical slots.
      // Check bounds too: a proxy may under-report its writable length.
      let slots = 0;
      for (const key of keys) {
        if (typeof key !== 'string') throw Error('Not plain data');
        const index = Number(key);
        if (
          Number.isInteger(index) &&
          index >= 0 &&
          index < 4294967295 &&
          String(index) === key
        ) {
          if (index >= arrayLength) throw Error('Array index exceeds length');
          slots++;
        }
      }
      if (slots !== arrayLength) throw Error('Sparse array');
    }
    for (const key of keys) {
      if (!valid() || typeof key !== 'string') throw Error('Not plain data');
      characters -= key.length;
      if (characters < 0) throw Error('Oversized keys');
      if (array && key === 'length') continue;
      const descriptor = Object.getOwnPropertyDescriptor(input, key);
      if (!valid() || !descriptor || !('value' in descriptor))
        throw Error('Accessor data');
      Object.defineProperty(output, key, {
        value: copy(descriptor.value, depth + 1),
        enumerable: true,
      });
    }
    seen.delete(input);
    if (!valid()) throw Error('Revoked');
    return Object.freeze(output);
  }
  return copy(value, 0);
}
export function ownSnapshot(
  value: NativeSnapshot,
  valid = () => true
): NativeSnapshot | undefined {
  try {
    return ownData(value, valid) as NativeSnapshot;
  } catch {
    return undefined;
  }
}
export function same(left: unknown, right: unknown): boolean {
  function canonical(value: unknown): unknown {
    if (Array.isArray(value)) return value.map(canonical);
    const r = record(value);
    return r
      ? Object.fromEntries(
          Object.keys(r)
            .sort()
            .map((k) => [k, canonical(r[k])])
        )
      : value;
  }
  return JSON.stringify(canonical(left)) === JSON.stringify(canonical(right));
}
export function validAmount(value: unknown): value is number {
  return (
    typeof value === 'number' &&
    Number.isFinite(value) &&
    value >= 0 &&
    value <= 1e9 &&
    Math.round(value * 100) / 100 === value
  );
}
export function expenseValue(value: unknown): Expense | undefined {
  const e = record(value);
  if (
    !e ||
    Object.keys(e).some(
      (k) => !['vendor', 'category', 'amount_usd', 'memo'].includes(k)
    )
  )
    return;
  for (const key of ['vendor', 'category', 'memo'])
    if (
      typeof e[key] !== 'string' ||
      !(e[key] as string).trim() ||
      (e[key] as string).length > (key === 'memo' ? 4000 : 500)
    )
      return;
  if (!validAmount(e['amount_usd'])) return;
  return Object.freeze({
    vendor: e['vendor'] as string,
    category: e['category'] as string,
    amount_usd: e['amount_usd'],
    memo: e['memo'] as string,
  });
}
function rootRows(s: NativeSnapshot): Transcript | undefined {
  if (!Array.isArray(s.transcript) || !Array.isArray(s.subagents)) return;
  const ids = new Set<string>(),
    calls = new Set<string>();
  for (const message of s.transcript) {
    const m = record(message);
    if (!m || typeof m['id'] !== 'string' || !m['id'] || ids.has(m['id']))
      return;
    ids.add(m['id']);
    if (
      m['subagentRunId'] !== undefined &&
      (typeof m['subagentRunId'] !== 'string' || !m['subagentRunId'])
    )
      return;
    const roles =
      typeof m['subagentRunId'] === 'string'
        ? [
            'user',
            'assistant',
            'tool',
            'developer',
            'system',
            'activity',
            'reasoning',
          ]
        : ['user', 'assistant', 'tool'];
    if (!roles.includes(m['role'] as string)) return;
    if (m['toolCalls'] !== undefined) {
      if (m['role'] !== 'assistant' || !Array.isArray(m['toolCalls'])) return;
      for (const call of m['toolCalls']) {
        const c = record(call),
          f = record(c?.['function']);
        if (
          !c ||
          typeof c['id'] !== 'string' ||
          !c['id'] ||
          calls.has(c['id']) ||
          c['type'] !== 'function' ||
          !f ||
          typeof f['name'] !== 'string' ||
          typeof f['arguments'] !== 'string'
        )
          return;
        calls.add(c['id']);
      }
    }
  }
  const rows = s.transcript.filter((m) => m.subagentRunId === undefined);
  const declarations = new Map<string, number>();
  rows.forEach((m, index) => {
    if (m.role === 'assistant')
      for (const call of m.toolCalls ?? []) declarations.set(call.id, index);
  });
  // Native metadata can authorize a pause without a redundant declaration.
  // When a declaration is present, its canonical result must follow it.
  if (
    rows.some(
      (m, index) =>
        m.role === 'tool' && (declarations.get(m.toolCallId) ?? -1) >= index
    )
  )
    return;
  return Object.freeze(rows);
}
function semantic(message: Transcript[number]) {
  return message.role === 'assistant' &&
    (message.content === '' || message.content === undefined)
    ? { ...message, content: '' }
    : message;
}
function history(past: Transcript, current: Transcript) {
  let cursor = 0;
  for (const message of current) {
    if (message.id !== past[cursor]?.id) continue;
    if (!same(semantic(past[cursor]), semantic(message))) return false;
    cursor++;
  }
  return cursor === past.length;
}
function declarationsAfterHuman(
  rows: Transcript,
  humanId: string,
  prefix: Transcript
) {
  const humanIndex = rows.findIndex((m) => m.id === humanId),
    earlier = new Set(submitCalls(prefix).map((c) => c.id));
  return (
    humanIndex >= 0 &&
    rows.every(
      (m, index) =>
        m.role !== 'assistant' ||
        (m.toolCalls ?? []).every(
          (c) => earlier.has(c.id) || index > humanIndex
        )
    )
  );
}
export function captureTurn(
  snapshot: NativeSnapshot,
  threadId: string,
  previous: string | undefined,
  prefix: Transcript,
  valid = () => true
): OwnedTurn | undefined {
  const s = ownSnapshot(snapshot, valid),
    rows = s && rootRows(s);
  if (
    !s ||
    !rows ||
    s.status !== 'running' ||
    s.decision ||
    typeof threadId !== 'string' ||
    !threadId ||
    typeof s.run?.id !== 'string' ||
    !s.run.id ||
    s.run.id === previous ||
    s.run.outcome !== undefined ||
    rows.length !== prefix.length + 1 ||
    !history(prefix, rows)
  )
    return;
  const human = rows.at(-1);
  if (
    !human ||
    human.role !== 'user' ||
    typeof human.content !== 'string' ||
    !human.content.trim() ||
    !valid()
  )
    return;
  return Object.freeze({
    threadId,
    runId: s.run.id,
    human,
    prefix: Object.freeze([...prefix]),
  });
}
function nativeInterrupt(value: unknown, now: number) {
  const i = record(value);
  if (
    !i ||
    typeof i['id'] !== 'string' ||
    !i['id'] ||
    i['reason'] !== 'tool_call' ||
    typeof i['toolCallId'] !== 'string' ||
    !i['toolCallId'] ||
    i['subagentRunId'] !== undefined
  )
    return;
  const expiry = i['expiresAt'];
  if (
    expiry !== undefined &&
    (typeof expiry !== 'string' ||
      !Number.isFinite(Date.parse(expiry)) ||
      Date.parse(expiry) <= now)
  )
    return;
  return i;
}
function callExpense(value: unknown, callId: string): Expense | undefined {
  const f = record(value),
    args = record(f?.['arguments']);
  if (
    !f ||
    f['call_id'] !== callId ||
    f['name'] !== 'submit_expense' ||
    !args ||
    Object.keys(args).length !== 1
  )
    return;
  return expenseValue(args['expense']);
}
function submitCalls(rows: Transcript) {
  return rows.flatMap((m) => (m.role === 'assistant' ? m.toolCalls ?? [] : []));
}
function argumentsMatch(value: string, expense: Expense) {
  try {
    return same(JSON.parse(value), { expense });
  } catch {
    return false;
  }
}
export function captureApproval(
  snapshot: NativeSnapshot,
  turn: OwnedTurn,
  valid = () => true
): ExpenseApproval | undefined {
  const s = ownSnapshot(snapshot, valid),
    rows = s && rootRows(s);
  if (!s || !rows) return;
  const d = s.decision,
    r = s.run,
    t = r?.terminal;
  if (
    s.status !== 'idle' ||
    r?.id !== turn.runId ||
    r.outcome !== 'paused' ||
    d?.kind !== 'native' ||
    typeof d.id !== 'string' ||
    !d.id ||
    d.attempt ||
    d.sourceRunId !== turn.runId ||
    !Array.isArray(d.interrupts) ||
    d.interrupts.length !== 1 ||
    t?.type !== 'RUN_FINISHED' ||
    record(t)?.['subagentRunId'] !== undefined ||
    t.threadId !== turn.threadId ||
    t.runId !== turn.runId ||
    t.outcome?.type !== 'interrupt' ||
    !Array.isArray(t.outcome.interrupts) ||
    t.outcome.interrupts.length !== 1 ||
    !history([...turn.prefix, turn.human], rows) ||
    !declarationsAfterHuman(rows, turn.human.id, turn.prefix)
  )
    return;
  const i = nativeInterrupt(d.interrupts[0], Date.now()),
    end = nativeInterrupt(t.outcome.interrupts[0], Date.now());
  if (!i || !end || !same(i, end)) return;
  const meta = record(record(i['metadata'])?.['agent_framework']),
    expense = callExpense(meta?.['function_call'], i['toolCallId'] as string);
  if (!meta || meta['type'] !== 'function_approval_request' || !expense) return;
  if (meta['value'] !== undefined) {
    const value = record(meta['value']);
    if (
      !value ||
      value['type'] !== meta['type'] ||
      !same(value['function_call'], meta['function_call'])
    )
      return;
  }
  const calls = submitCalls(rows),
    matches = calls.filter((c) => c.id === i['toolCallId']);
  if (
    matches.length > 1 ||
    matches.some(
      (c) =>
        c.function.name !== 'submit_expense' ||
        !argumentsMatch(c.function.arguments, expense)
    ) ||
    calls.some(
      (c) =>
        c.function.name === 'submit_expense' &&
        c.id !== i['toolCallId'] &&
        !turn.prefix.some(
          (m) =>
            m.role === 'assistant' && m.toolCalls?.some((p) => p.id === c.id)
        )
    ) ||
    rows.some((m) => m.role === 'tool' && m.toolCallId === i['toolCallId']) ||
    rows.filter((m) => m.role === 'user').length !==
      turn.prefix.filter((m) => m.role === 'user').length + 1 ||
    !valid()
  )
    return;
  // PauseId is an immutable branded string. Retain the original value separately from the owned observation.
  try {
    const originalDecision = Object.getOwnPropertyDescriptor(
      snapshot,
      'decision'
    )?.value;
    if (!valid()) return;
    const pause =
      originalDecision &&
      (Object.getOwnPropertyDescriptor(originalDecision, 'id')
        ?.value as Parameters<Session['resume']>[0]);
    if (!valid() || pause !== d.id) return;
    return Object.freeze({
      threadId: turn.threadId,
      sourceRunId: turn.runId,
      pause,
      interruptId: i['id'] as string,
      toolCallId: i['toolCallId'] as string,
      expense,
      transcript: rows,
      observation: s,
    });
  } catch {
    return undefined;
  }
}
/** Prediction and unrelated child observations never change root decision authority. */
export function sameApproval(a: ExpenseApproval, b: ExpenseApproval): boolean {
  return (
    a.pause === b.pause &&
    a.threadId === b.threadId &&
    a.sourceRunId === b.sourceRunId &&
    a.interruptId === b.interruptId &&
    a.toolCallId === b.toolCallId &&
    same(a.expense, b.expense) &&
    same(a.transcript, b.transcript) &&
    same(a.observation.decision, b.observation.decision) &&
    same(a.observation.run, b.observation.run)
  );
}
export function confirmResume(
  snapshot: NativeSnapshot,
  approval: ExpenseApproval,
  runId: string,
  approved: boolean,
  valid = () => true
): { readonly transcript: Transcript; readonly result: string } | undefined {
  const s = ownSnapshot(snapshot, valid),
    rows = s && rootRows(s);
  if (
    !s ||
    !rows ||
    !runId ||
    runId === approval.sourceRunId ||
    s.status !== 'idle' ||
    s.decision ||
    s.run?.id !== runId ||
    s.run.outcome !== 'success'
  )
    return;
  const t = s.run.terminal;
  const humanIndex = approval.transcript.map((m) => m.role).lastIndexOf('user');
  if (
    t?.type !== 'RUN_FINISHED' ||
    record(t)?.['subagentRunId'] !== undefined ||
    t.threadId !== approval.threadId ||
    t.runId !== runId ||
    (t.outcome !== undefined && t.outcome.type !== 'success') ||
    (t.outcome?.type === 'success' && !!t.outcome.pendingToolCallIds?.length) ||
    !history(approval.transcript, rows) ||
    humanIndex < 0 ||
    !declarationsAfterHuman(
      rows,
      approval.transcript[humanIndex].id,
      approval.transcript.slice(0, humanIndex)
    )
  )
    return;
  const calls = submitCalls(rows).filter((c) => c.id === approval.toolCallId),
    results = rows.filter(
      (m) => m.role === 'tool' && m.toolCallId === approval.toolCallId
    );
  const e = approval.expense,
    expected = approved
      ? `Expense recorded: $${e.amount_usd.toFixed(2)} to ${e.vendor} (${
          e.category
        }) — queued for reimbursement.`
      : 'Error: Tool call invocation was rejected by user.';
  // A native approval may pause a batch before its read-only lookup resolves.
  // Only declarations already owned at the pause may gain a result on resume.
  const pending = submitCalls(approval.transcript).filter(
    (c) =>
      c.id !== approval.toolCallId &&
      !approval.transcript.some(
        (m) => m.role === 'tool' && m.toolCallId === c.id
      )
  );
  for (const call of pending) {
    try {
      if (
        call.function.name !== 'lookup_expense_policy' ||
        !same(JSON.parse(call.function.arguments), { category: e.category })
      )
        return;
    } catch {
      return;
    }
    const lookup = rows.filter(
      (m) => m.role === 'tool' && m.toolCallId === call.id
    );
    if (
      lookup.length !== 1 ||
      lookup[0].role !== 'tool' ||
      lookup[0].error ||
      typeof lookup[0].content !== 'string' ||
      !lookup[0].content.trim()
    )
      return;
  }
  if (
    calls.length !== 1 ||
    calls[0].function.name !== 'submit_expense' ||
    !argumentsMatch(calls[0].function.arguments, e) ||
    results.length !== 1 ||
    results[0].role !== 'tool' ||
    results[0].content !== expected ||
    results[0].error ||
    rows.some(
      (m) =>
        m.role === 'user' && !approval.transcript.some((p) => p.id === m.id)
    ) ||
    rows.some(
      (m) =>
        m.role === 'tool' &&
        m.toolCallId !== approval.toolCallId &&
        !approval.transcript.some((p) => p.id === m.id) &&
        !pending.some((c) => c.id === m.toolCallId)
    ) ||
    submitCalls(rows).some(
      (c) =>
        c.id !== approval.toolCallId &&
        !submitCalls(approval.transcript).some((p) => p.id === c.id)
    ) ||
    !valid()
  )
    return;
  return Object.freeze({ transcript: rows, result: results[0].content });
}

Resume the native session

The decision uses top-level native session.resume(pauseId, [{ interruptId, status: 'resolved', payload: { approved } }], { signal }). The application preserves the original native pause and validates its current run, tool call, and expense correlation before accepting a decision.

application.ts
import type { Session } from '@threadplane/ag-ui';
import {
  captureApproval,
  captureTurn,
  confirmResume,
  ownSnapshot,
  same,
  sameApproval,
  type ExpenseApproval,
  type NativeSnapshot,
  type OwnedTurn,
} from './approval-policy';
import { projectExpense, type ExpenseDraft } from './expense-projection';
export interface MafSnapshot {
  readonly generation: number;
  readonly threadId: string | null;
  readonly busy: boolean;
  readonly canSend: boolean;
  readonly canRespond: boolean;
  readonly rows: NativeSnapshot['transcript'];
  readonly draft: ExpenseDraft | undefined;
  readonly approval: ExpenseApproval | undefined;
  readonly result: string | null;
  readonly status:
    | 'ready'
    | 'running'
    | 'pending'
    | 'approved'
    | 'declined'
    | 'unconfirmed'
    | 'cleanup-failed'
    | 'replacing'
    | 'disposed';
  readonly error: string | null;
}
type Methods = { [K in keyof Session]: Session[K] };
type Owner = {
  readonly candidate: Session;
  readonly threadId: string;
  readonly methods: Methods;
  readonly seenRuns: Set<string>;
  closed: boolean;
  release?: () => void;
  turn?: OwnedTurn;
  approval?: ExpenseApproval;
  transcript: NativeSnapshot['transcript'];
  previous?: string;
  intent?: 'submit' | 'resume';
  resumeRunId?: string;
  approved?: boolean;
  unsafe: boolean;
};
/** Owns admission, observation and lifetime. Native Session alone executes the protocol. */
export function createMafApplication(options: {
  readonly sessionFactory: (threadId: string) => Session | Promise<Session>;
  readonly threadIdFactory?: () => string;
}) {
  const listeners = new Set<() => void>(),
    cleanups = new Set<Promise<void>>();
  let primary: Owner | undefined,
    operation: AbortController | undefined,
    disposed = false,
    cleanupFailed = false,
    revoking = false,
    disposal: Promise<void> | undefined;
  let snapshot: MafSnapshot = Object.freeze({
    generation: 0,
    threadId: null,
    busy: false,
    canSend: true,
    canRespond: false,
    rows: Object.freeze([]),
    draft: undefined,
    approval: undefined,
    result: null,
    status: 'ready',
    error: null,
  });
  const cleanupFailure: Partial<MafSnapshot> = Object.freeze({
    canSend: false,
    canRespond: false,
    approval: undefined,
    result: null,
    status: 'cleanup-failed',
    error:
      'The conversation could not close safely. Reload the preview to continue.',
  });
  function failCleanup() {
    // A successful fallback cannot erase an earlier owned cleanup failure.
    cleanupFailed = true;
    publish(cleanupFailure);
  }
  function publish(update: Partial<MafSnapshot>) {
    if (disposed) return;
    snapshot = Object.freeze({
      ...snapshot,
      ...update,
      ...(cleanupFailed ? cleanupFailure : {}),
    });
    for (const notify of [...listeners]) {
      try {
        notify();
      } catch {
        /* An observer cannot prevent revocation. */
      }
    }
  }
  function track(promise: Promise<unknown>) {
    const done = promise.then(
      () => undefined,
      () => undefined
    );
    cleanups.add(done);
    void done.then(() => cleanups.delete(done));
    return done;
  }
  async function drain() {
    while (cleanups.size) await Promise.all([...cleanups]);
  }
  function current(p: Owner) {
    return !disposed && !cleanupFailed && primary === p && !p.closed;
  }
  function cleanupCandidate(
    candidate: unknown,
    methods?: Partial<Methods>,
    release?: () => void
  ) {
    // Authority and the rendered controls have already been revoked by the caller.
    const done = track(
      Promise.resolve()
        .then(async () => {
          let dispose = methods?.dispose;
          try {
            dispose ??= (candidate as Session)?.dispose;
          } catch {
            failCleanup();
          }
          if (typeof dispose === 'function') {
            try {
              await dispose.call(candidate);
              return;
            } catch {
              failCleanup();
            }
          }
          let stop = methods?.stop;
          try {
            stop ??= (candidate as Session)?.stop;
          } catch {
            failCleanup();
          }
          if (typeof stop === 'function') {
            try {
              await stop.call(candidate);
            } catch {
              failCleanup();
            }
          } else failCleanup();
        })
        .catch(() => {
          // Preserve failure before the settlement-only tracking barrier.
          failCleanup();
        })
    );
    try {
      release?.();
    } catch {
      // The cleanup barrier is registered before publishing to reentrant observers.
      failCleanup();
    }
    return done;
  }
  function close(p: Owner) {
    p.closed = true;
    const release = p.release;
    p.release = undefined;
    return cleanupCandidate(p.candidate, p.methods, release);
  }
  function fail(p?: Owner, bootstrap = false) {
    if (p && !current(p)) return;
    let prepared: (() => void) | undefined;
    if (p) {
      p.unsafe = true;
      p.approval = undefined;
      if (bootstrap) {
        primary = undefined;
        p.closed = true;
        // Observers may dispose after detachment but before release. Keep every
        // disposal behind preparation until candidate cleanup is registered.
        track(
          new Promise<void>((resolve) => {
            prepared = resolve;
          })
        );
      }
    }
    publish({
      canSend: false,
      canRespond: false,
      approval: undefined,
      result: null,
      status: 'unconfirmed',
      error:
        'The expense outcome could not be confirmed. Start a new conversation.',
    });
    if (p && bootstrap) {
      try {
        void close(p);
      } finally {
        prepared?.();
      }
    }
  }
  function read(p: Owner, valid: () => boolean) {
    if (!valid()) return;
    const native = p.methods.getSnapshot.call(p.candidate);
    if (!valid()) return;
    return ownSnapshot(native, valid);
  }
  function capture(p: Owner, valid: () => boolean) {
    const native = read(p, valid);
    if (
      !native ||
      !valid() ||
      !Array.isArray(native.transcript) ||
      !Array.isArray(native.subagents)
    )
      return;
    if (native.status === 'error') p.unsafe = true;
    if (p.intent === 'submit' && !p.turn) {
      const turn = captureTurn(
        native,
        p.threadId,
        p.previous,
        p.transcript,
        valid
      );
      if (turn) {
        if (p.seenRuns.has(turn.runId)) p.unsafe = true;
        else {
          p.turn = turn;
          p.seenRuns.add(turn.runId);
        }
      }
    }
    if (
      p.intent === 'resume' &&
      !p.resumeRunId &&
      p.approval &&
      native.status === 'running' &&
      typeof native.run?.id === 'string' &&
      native.run.id !== p.approval.sourceRunId &&
      native.decision?.kind === 'native' &&
      native.decision.id === p.approval.pause &&
      native.decision.sourceRunId === p.approval.sourceRunId &&
      native.decision.attempt?.runId === native.run?.id
    ) {
      if (p.seenRuns.has(native.run.id)) p.unsafe = true;
      else {
        p.resumeRunId = native.run.id;
        p.seenRuns.add(native.run.id);
      }
    }
    const approval =
      p.intent === 'submit' && p.turn && !p.unsafe
        ? captureApproval(native, p.turn, valid)
        : undefined;
    const confirmed =
      p.intent === 'resume' &&
      p.approval &&
      p.resumeRunId &&
      typeof p.approved === 'boolean' &&
      !p.unsafe
        ? confirmResume(native, p.approval, p.resumeRunId, p.approved, valid)
        : undefined;
    return valid()
      ? {
          native,
          rows: Object.freeze(
            native.transcript.filter((m) => m.subagentRunId === undefined)
          ),
          draft: projectExpense(native.state),
          approval,
          confirmed,
        }
      : undefined;
  }
  function observe(p: Owner) {
    const valid = () => current(p) && !operation?.signal.aborted;
    try {
      const result = capture(p, valid);
      if (!valid()) return;
      if (!result) {
        fail(p);
        return;
      }
      if (
        snapshot.canRespond &&
        (!result.approval ||
          !p.approval ||
          !sameApproval(result.approval, p.approval))
      ) {
        fail(p);
        return;
      }
      publish({ rows: result.rows, draft: result.draft });
    } catch {
      if (valid()) fail(p);
    }
  }
  function claim(status: 'running' | 'replacing') {
    const controller = new AbortController();
    operation = controller;
    const valid = () =>
      !disposed &&
      !cleanupFailed &&
      operation === controller &&
      !controller.signal.aborted;
    publish({
      busy: true,
      canSend: false,
      canRespond: false,
      approval: undefined,
      result: null,
      status,
      error: null,
    });
    return { controller, valid };
  }
  function finish(controller: AbortController) {
    if (!disposed && operation === controller) {
      operation = undefined;
      publish({ busy: false });
    }
  }
  async function install(valid: () => boolean): Promise<Owner | undefined> {
    if (!valid()) return;
    const threadIdFactory =
      options.threadIdFactory ?? (() => crypto.randomUUID());
    if (!valid()) return;
    const threadId = threadIdFactory();
    if (!valid()) return;
    if (typeof threadId !== 'string' || !threadId || threadId.length > 500)
      throw TypeError('A thread identifier is required');
    const factory = options.sessionFactory;
    if (!valid()) return;
    // Track late bootstrap so New/dispose cannot finish ahead of candidate cleanup.
    let resolveCandidate!: (candidate: Session | Promise<Session>) => void,
      rejectCandidate!: (error: unknown) => void;
    const candidatePromise = new Promise<Session>((resolve, reject) => {
      resolveCandidate = resolve;
      rejectCandidate = reject;
    });
    track(
      candidatePromise.then((candidate) => {
        if (!valid()) return cleanupCandidate(candidate);
        return undefined;
      })
    );
    try {
      resolveCandidate(factory(threadId));
    } catch (error) {
      rejectCandidate(error);
    }
    const candidate = await candidatePromise;
    if (!valid()) return;
    const methods: Partial<Methods> = {};
    let release: (() => void) | undefined,
      p: Owner | undefined,
      cleanupAccessFailed = false;
    try {
      if (
        candidate === null ||
        (typeof candidate !== 'object' && typeof candidate !== 'function')
      )
        throw TypeError('A native Session is required');
      for (const name of [
        'getSnapshot',
        'subscribe',
        'submit',
        'resume',
        'stop',
        'dispose',
      ] as const) {
        let method: unknown;
        try {
          method = candidate[name];
        } catch (error) {
          cleanupAccessFailed = name === 'stop' || name === 'dispose';
          throw error;
        }
        if (!valid()) {
          void cleanupCandidate(candidate, methods);
          return;
        }
        if (typeof method !== 'function')
          throw TypeError('A complete native Session is required');
        Object.defineProperty(methods, name, {
          value: method,
          enumerable: true,
        });
      }
      p = {
        candidate,
        threadId,
        methods: methods as Methods,
        seenRuns: new Set(),
        closed: false,
        transcript: Object.freeze([]),
        unsafe: false,
      };
      const selected = p;
      const result = p.methods.subscribe.call(candidate, () => {
        if (primary === selected) observe(selected);
      });
      if (typeof result !== 'function')
        throw TypeError('A callable release is required');
      release = result;
      if (!valid()) {
        p.closed = true;
        void cleanupCandidate(candidate, methods, release);
        return;
      }
      // Only a complete callable candidate with its release in hand can become authoritative.
      p.release = release;
      primary = p;
      return p;
    } catch (error) {
      if (p) p.closed = true;
      void cleanupCandidate(candidate, methods, release);
      // Register owned cleanup before notifying reentrant observers. A later
      // successful disposal cannot erase an observed cleanup accessor failure.
      if (cleanupAccessFailed) failCleanup();
      throw error;
    }
  }
  async function submit(message: string, generation = snapshot.generation) {
    if (
      disposed ||
      cleanupFailed ||
      revoking ||
      snapshot.busy ||
      !snapshot.canSend ||
      generation !== snapshot.generation ||
      typeof message !== 'string' ||
      !message.trim() ||
      message.length > 16000
    )
      return;
    const { controller, valid } = claim('running');
    let p = primary;
    let bootstrapping = true;
    try {
      if (!valid()) return;
      await drain();
      if (!valid()) return;
      p ??= await install(valid);
      if (!p || !valid() || !current(p)) return;
      const owner = p,
        ownedValid = () => valid() && current(owner);
      publish({ threadId: p.threadId });
      if (!ownedValid()) return;
      const initial = read(p, ownedValid);
      if (!ownedValid()) return;
      if (
        !initial ||
        initial.status !== 'idle' ||
        initial.decision ||
        p.unsafe ||
        !same(
          initial.transcript.filter((m) => m.subagentRunId === undefined),
          p.transcript
        )
      ) {
        fail(p, true);
        return;
      }
      bootstrapping = false;
      p.previous = initial.run?.id;
      p.turn = undefined;
      p.intent = 'submit';
      p.approval = undefined;
      const pending = p.methods.submit.call(p.candidate, message, {
        signal: controller.signal,
      });
      void pending.catch(() => undefined);
      if (!ownedValid()) return;
      observe(p);
      const outcome = await pending;
      if (!ownedValid()) return;
      const result = capture(p, ownedValid);
      if (!ownedValid()) return;
      if (outcome !== 'paused' || !result?.approval || p.unsafe) {
        fail(p);
        return;
      }
      p.approval = result.approval;
      publish({
        rows: result.rows,
        draft: result.draft,
        approval: p.approval,
        canRespond: true,
        canSend: false,
        status: 'pending',
      });
    } catch {
      if (valid()) fail(p, bootstrapping);
    } finally {
      finish(controller);
    }
  }
  async function decide(
    approval: ExpenseApproval,
    action: 'approve' | 'decline',
    generation = snapshot.generation
  ) {
    const p = primary;
    if (
      disposed ||
      cleanupFailed ||
      revoking ||
      snapshot.busy ||
      !snapshot.canRespond ||
      generation !== snapshot.generation ||
      !p ||
      p.closed ||
      p.unsafe ||
      p.approval !== approval ||
      snapshot.approval !== approval ||
      !p.turn ||
      (action !== 'approve' && action !== 'decline')
    )
      return;
    // Claim synchronously before getters, native execution or observer notification.
    const { controller, valid } = claim('running'),
      ownedValid = () => valid() && current(p);
    try {
      if (!ownedValid()) return;
      const native = read(p, ownedValid),
        fresh = native && captureApproval(native, p.turn, ownedValid);
      if (!ownedValid()) return;
      if (!fresh || !sameApproval(fresh, approval)) {
        fail(p);
        return;
      }
      p.intent = 'resume';
      p.resumeRunId = undefined;
      p.approved = action === 'approve';
      const pending = p.methods.resume.call(
        p.candidate,
        approval.pause,
        [
          {
            interruptId: approval.interruptId,
            status: 'resolved',
            payload: { approved: p.approved },
          },
        ],
        { signal: controller.signal }
      );
      void pending.catch(() => undefined);
      if (!ownedValid()) return;
      observe(p);
      const outcome = await pending;
      if (!ownedValid()) return;
      const result = capture(p, ownedValid);
      if (!ownedValid()) return;
      if (outcome !== 'success' || !result?.confirmed || p.unsafe) {
        fail(p);
        return;
      }
      p.transcript = result.confirmed.transcript;
      p.approval = undefined;
      publish({
        rows: result.rows,
        draft: result.draft,
        approval: undefined,
        canRespond: false,
        canSend: true,
        status: p.approved ? 'approved' : 'declined',
        result: result.confirmed.result,
        error: null,
      });
    } catch {
      if (ownedValid()) fail(p);
    } finally {
      finish(controller);
    }
  }
  async function revoke(replacing: boolean) {
    if (disposed || cleanupFailed || revoking) return;
    revoking = true;
    const previous = operation,
      p = primary;
    operation = undefined;
    primary = undefined;
    if (p) {
      p.closed = true;
      p.approval = undefined;
    }
    // A view notification can dispose while the old owner is already detached.
    // Keep that disposal behind preparation until the old cleanup is registered.
    let prepared!: () => void;
    track(
      new Promise<void>((resolve) => {
        prepared = resolve;
      })
    );
    // Clear controls and result before abort/release/cleanup can inspect or reenter.
    const { controller, valid } = claim(replacing ? 'replacing' : 'running');
    publish({
      generation: snapshot.generation + 1,
      threadId: replacing ? null : snapshot.threadId,
      rows: replacing ? Object.freeze([]) : snapshot.rows,
      draft: replacing ? undefined : snapshot.draft,
      approval: undefined,
      result: null,
      canRespond: false,
      canSend: false,
      status: replacing ? 'replacing' : 'unconfirmed',
      error: replacing
        ? null
        : 'The operation stopped. Start a new conversation.',
    });
    try {
      previous?.abort();
      if (p) void close(p);
    } finally {
      prepared();
    }
    try {
      await drain();
      if (valid() && replacing) publish({ canSend: true, status: 'ready' });
    } finally {
      revoking = false;
      finish(controller);
    }
  }
  function dispose(): Promise<void> {
    if (disposal) return disposal;
    let settled!: () => void;
    disposal = new Promise<void>((resolve) => {
      settled = resolve;
    });
    disposed = true;
    const previous = operation,
      p = primary;
    operation = undefined;
    primary = undefined;
    if (p) {
      p.closed = true;
      p.approval = undefined;
    }
    snapshot = Object.freeze({
      ...snapshot,
      generation: snapshot.generation + 1,
      busy: false,
      canSend: false,
      canRespond: false,
      approval: undefined,
      result: null,
      status: 'disposed',
    });
    listeners.clear();
    previous?.abort();
    if (p) void close(p);
    void drain().then(settled);
    return disposal;
  }
  return {
    getSnapshot: () => snapshot,
    subscribe(notify: () => void) {
      if (disposed) return () => undefined;
      listeners.add(notify);
      return () => {
        listeners.delete(notify);
      };
    },
    submit,
    decide,
    newConversation: () => revoke(true),
    stop: () => revoke(false),
    dispose,
  };
}

Native text and predictive expense state are projected for display. Observed backend result shows the exact literal tool text separately from assistant prose. Confirmation requires the current normal root RUN_FINISHED, a matching authoritative snapshot tool result, and a settled continuation. A normal terminal may omit its outcome field. A legitimate Decline result may appear only in the snapshot, without a TOOL_CALL_RESULT event. HTTP completion, a local decision, and assistant prose alone cannot confirm an expense. An unconfirmed outcome requires New conversation and never replays an accepted decision.

expense-projection.ts
import { ownData, record, validAmount, type Expense } from './approval-policy';
export type ExpenseDraft = Readonly<Partial<Expense>>;
/** Shared state is predictive display only, including when contributed by a child. */
export function projectExpense(state: unknown): ExpenseDraft | undefined {
  try {
    const e = record(record(ownData(state))?.['expense']);
    if (!e) return;
    const draft: Partial<{ -readonly [K in keyof Expense]: Expense[K] }> = {};
    for (const key of ['vendor', 'category', 'memo'] as const)
      if (
        typeof e[key] === 'string' &&
        e[key].length <= (key === 'memo' ? 4000 : 500)
      )
        draft[key] = e[key];
    if (validAmount(e['amount_usd'])) draft.amount_usd = e['amount_usd'];
    return Object.freeze(draft);
  } catch {
    return undefined;
  }
}

Stop and start fresh

Stop revokes current controls. New conversation clears local state and waits for owned cleanup before another Send. Docs, Code, and Run retain the same iframe and unsent draft without starting a request. Leaving the page or switching frontend disposes the Session. Reload the preview after an owned cleanup failure; Send, decisions, and New conversation remain disabled for that application lifetime. There is no automatic reconnect, replay, saved-history restoration, or durable cross-reload claim.

Use the shared service

Shared runtime uses /runtimes/microsoft-agent-framework/agent; this frontend lives at /runtimes/microsoft-agent-framework/react/ and uses the AG-UI adapter. A configured AG-UI Developer runtime is supported. Runtime addresses and credentials remain outside the visible preview.

connection.ts
import { createSession } from '@threadplane/ag-ui';
import { createMafApplication } from './application';
// eslint-disable-next-line @nx/enforce-module-boundaries -- The installed build copies this neutral internal bridge.
import type { RuntimeBridgeConfiguration } from '../../../../../libs/cockpit-runtime-bridge/src/index';
 
export function mafConnection(configuration: RuntimeBridgeConfiguration): {
  readonly url: string;
  readonly label: 'Shared runtime' | 'Developer runtime';
} {
  if (
    configuration.status === 'error' ||
    (configuration.status === 'configured' &&
      configuration.target.kind === 'langsmith')
  )
    throw new Error('Runtime configuration failed.');
  const target =
    configuration.status === 'configured' &&
    configuration.target.kind === 'ag-ui'
      ? configuration.target
      : null;
  return {
    url:
      target?.endpoint ??
      new URL(
        '/runtimes/microsoft-agent-framework/agent',
        window.location.origin
      ).href,
    label: target ? 'Developer runtime' : 'Shared runtime',
  };
}
export function createConnectedApplication(
  connection: ReturnType<typeof mafConnection>
) {
  return createMafApplication({
    sessionFactory: (threadId) =>
      createSession({ threadId, url: connection.url }),
  });
}
agent.py
"""Expense approval copilot — Microsoft Agent Framework backend.
 
A genuinely non-LangGraph AG-UI backend exercising the neutral Agent
contract surfaces measured in the 2026-08-31 runtime-portability matrix:
 
- messages: streamed assistant text.
- tool calls: ``lookup_expense_policy`` executes server-side with no pause.
- shared state: the ``expense`` argument of ``submit_expense`` streams
  predictively into frontend state via ``predict_state_config``
  (STATE_SNAPSHOT / STATE_DELTA).
- interrupts: ``submit_expense`` requires approval
  (``approval_mode="always_require"``), so the run finishes with the
  protocol-standard ``RUN_FINISHED.outcome = {type: 'interrupt', ...}`` and
  resumes from the client's top-level ``resume`` entries.
 
- subagents: ``research_policy`` delegates to the tool-less
  ``policy_researcher`` specialist Agent and streams its deltas through the
  ``delegation_*`` helpers in src/subagent_emitter.py, which merge standard
  ``SUBAGENT_*`` + attributed child ``TEXT_MESSAGE_*`` events into the run
  stream at the run-wrapper seam (the MAF bridge natively emits NOTHING for
  nested-agent activity inside a tool — measured red upstream and in
  docs/wire-capture-subagents.md).
 
Model client: Azure OpenAI is the DEFAULT path — when
``AZURE_OPENAI_ENDPOINT`` is set the client routes to Azure (key auth via
``AZURE_OPENAI_API_KEY``; deployment via ``AZURE_OPENAI_MODEL``;
``AZURE_OPENAI_API_VERSION`` optional). Without it, the plain OpenAI client
is used on ``OPENAI_API_KEY`` (honoring ``OPENAI_BASE_URL``, which is how
the aimock e2e harness intercepts calls). See .env.example.
"""
 
import os
 
from agent_framework import Agent, tool
from agent_framework.openai import OpenAIChatCompletionClient
from pydantic import BaseModel, Field
 
from . import subagent_emitter
from .snapshot_compat import HistoricalToolSnapshotAgent
 
_POLICIES = {
    "meals": {"limit_usd": 300, "receipt_required_over_usd": 25, "notes": "Team meals require attendee count in the memo."},
    "travel": {"limit_usd": 1500, "receipt_required_over_usd": 0, "notes": "Book through the travel portal when possible."},
    "equipment": {"limit_usd": 800, "receipt_required_over_usd": 0, "notes": "Laptops and peripherals need an asset tag."},
    "software": {"limit_usd": 500, "receipt_required_over_usd": 0, "notes": "Annual subscriptions need manager pre-approval."},
}
 
 
class Expense(BaseModel):
    """A structured expense report entry."""
 
    vendor: str = Field(..., description="Merchant or vendor name, e.g. 'Blue Finch Bistro'.")
    category: str = Field(..., description="One of: meals, travel, equipment, software, other.")
    amount_usd: float = Field(..., description="Total amount in USD.")
    memo: str = Field(..., description="One-line justification, including attendee count for meals.")
 
 
# region policy-tool
@tool(
    name="lookup_expense_policy",
    description="Look up the reimbursement policy for an expense category.",
)
def lookup_expense_policy(category: str) -> str:
    """Return the reimbursement policy for a category.
 
    Args:
        category: Expense category, e.g. 'meals' or 'travel'.
 
    Returns:
        A short policy summary string.
    """
    policy = _POLICIES.get(category.strip().lower())
    if policy is None:
        return f"No specific policy for '{category}'; the general limit is $200 with receipts required."
    return (
        f"Policy for {category}: limit ${policy['limit_usd']} per expense, "
        f"receipts required over ${policy['receipt_required_over_usd']}. {policy['notes']}"
    )
# endregion
 
 
# region approval-tool
@tool(
    name="submit_expense",
    description="Submit an expense report entry for reimbursement. Requires human approval.",
    approval_mode="always_require",
)
def submit_expense(expense: Expense) -> str:
    """Submit the expense for reimbursement once a human approves it.
 
    Args:
        expense: The complete expense entry (vendor, category, amount_usd, memo).
 
    Returns:
        A confirmation string with the recorded amount.
    """
    # On the approval-resume path the framework replays the stored tool-call
    # arguments as plain dicts rather than re-validating through pydantic —
    # normalize before reading attributes.
    entry = expense if isinstance(expense, Expense) else Expense.model_validate(expense)
    return (
        f"Expense recorded: ${entry.amount_usd:.2f} to {entry.vendor} "
        f"({entry.category}) — queued for reimbursement."
    )
# endregion
 
 
_INSTRUCTIONS = """You are an expense approval copilot.
 
Before recommending whether to submit an expense, delegate the policy
research to the specialist by calling `research_policy` with the category
and amount.
 
When the user asks to file an expense:
1. FIRST call `lookup_expense_policy` with the expense category.
2. THEN call `submit_expense` with the complete structured expense
   (vendor, category, amount_usd, memo). Do not output prose alongside the
   `submit_expense` call — the user will be shown an approval card.
3. After the human approves and the tool executes, confirm in one short
   sentence that the expense has been submitted for reimbursement, naming
   the amount and vendor.
 
If the human rejects the submission, acknowledge in one short sentence that
the expense was not submitted.
 
Keep every response brief and factual. Never invent policy details — use the
policy tool.
"""
 
 
# region model-client
def build_chat_client() -> OpenAIChatCompletionClient:
    """Azure OpenAI by default; plain OpenAI when Azure env is absent.
 
    Passing `azure_endpoint` explicitly is the constructor's strongest Azure
    signal — it wins even when OPENAI_API_KEY is also set, which makes Azure
    the default whenever it is configured. Key, deployment (model), and API
    version resolve from AZURE_OPENAI_* env vars.
    """
    azure_endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT")
    if azure_endpoint:
        return OpenAIChatCompletionClient(
            model=os.environ.get("AZURE_OPENAI_MODEL"),
            azure_endpoint=azure_endpoint,
        )
    # Passing api_key explicitly forces OpenAI routing even when no
    # OPENAI_API_KEY env var is set; without it the constructor falls back to
    # Azure env resolution and raises at import time. A placeholder key fails
    # properly at request time (401) instead of at module import.
    return OpenAIChatCompletionClient(
        model=os.environ.get("OPENAI_CHAT_MODEL", "gpt-4o-mini"),
        api_key=os.environ.get("OPENAI_API_KEY", "unset-openai-api-key"),
    )
# endregion
 
 
# region delegation
policy_researcher = Agent(
    name="policy_researcher",
    instructions=(
        "You are an expense-policy researcher. Given an expense category and "
        "amount, summarize the applicable policy rules in 3 short bullets."
    ),
    client=build_chat_client(),
)
 
 
@tool(
    name="research_policy",
    description="Delegate policy research for this expense to a specialist.",
)
async def research_policy(category: str, amount: float) -> str:
    """Delegate policy research for this expense to a specialist.
 
    Streams the ``policy_researcher`` specialist and mirrors each text delta
    onto the AG-UI wire as attributed SUBAGENT_* / TEXT_MESSAGE_* events via
    src/subagent_emitter.py (no-ops outside the wrapped run).
 
    Args:
        category: Expense category, e.g. 'meals' or 'travel'.
        amount: Expense amount in USD.
 
    Returns:
        The specialist's complete policy summary.
    """
    # Deterministically recorded by the run wrapper's pump before this body
    # runs (the bridge streams TOOL_CALL_START/ARGS/END first); None when
    # invoked outside a wrapped run.
    tid = subagent_emitter.current_tool_call_id("research_policy")
    subagent_emitter.delegation_started(tid, policy_researcher.name)
    parts: list[str] = []
    try:
        prompt = (
            f"Expense category: {category}. Amount: ${amount:.2f}. "
            "Summarize the applicable policy rules."
        )
        async for update in policy_researcher.run(prompt, stream=True):
            text = update.text
            if text:
                parts.append(text)
                subagent_emitter.delegation_delta(tid, text)
    except Exception as exc:
        subagent_emitter.delegation_error(tid, str(exc))
        raise
    subagent_emitter.delegation_finished(tid)
    return "".join(parts)
# endregion
 
 
# region bridge
agent = HistoricalToolSnapshotAgent(
    agent=Agent(
        name="expense_approval_copilot",
        instructions=_INSTRUCTIONS,
        client=build_chat_client(),
        tools=[lookup_expense_policy, research_policy, submit_expense],
    ),
    name="ExpenseApprovalCopilot",
    description="Files expense reports with policy lookup, shared state, and human approval.",
    state_schema={
        "expense": {"type": "object", "description": "The expense entry being drafted."},
    },
    predict_state_config={
        "expense": {"tool": "submit_expense", "tool_argument": "expense"},
    },
    require_confirmation=False,
)
# endregion
server.py
from fastapi import FastAPI
from agent_framework_ag_ui import add_agent_framework_fastapi_endpoint
 
from .agent import agent
from .subagent_emitter import wrap_agent_run
 
app = FastAPI(title="cockpit-runtimes-microsoft-agent-framework")
# The wrapper is the SUBAGENT_* injection seam: the endpoint consumes
# protocol_runner.run, and wrap_agent_run merges the delegation tool's
# enqueued child events into that stream (src/subagent_emitter.py).
wrapped_agent = wrap_agent_run(agent)
add_agent_framework_fastapi_endpoint(app, wrapped_agent, path="/agent")
 
 
@app.get("/ok")
def ok() -> dict:
    return {"ok": True}

Build and verify from source

npx nx build cockpit-runtimes-microsoft-agent-framework-react
npx nx e2e cockpit-runtimes-microsoft-agent-framework-react

The e2e target serializes frozen Python 3.12 fixture preparation before running the unchanged generated service against a deterministic offline OpenAI model seam. The fixture owns isolated temporary dependencies and HTTP processes, verifies actual native pause/resume and literal results, and closes its owned resources. These checks prove local HTTP/SSE interoperability; they do not prove a live model-provider request. Docs, Code, and Run select this same authored implementation.

project.json
{
  "name": "cockpit-runtimes-microsoft-agent-framework-react",
  "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "cockpit/runtimes/microsoft-agent-framework/react/src",
  "projectType": "application",
  "tags": ["scope:cockpit-examples", "scope:cockpit-e2e", "framework:react"],
  "targets": {
    "prepare-fixture": {
      "executor": "nx:run-commands",
      "cache": false,
      "options": {
        "command": "node scripts/react-cockpit/prepare-native-maf-fixture.mjs"
      }
    },
    "e2e": {
      "executor": "@nx/playwright:playwright",
      "cache": false,
      "dependsOn": ["prepare-fixture", "build"],
      "options": {
        "config": "cockpit/runtimes/microsoft-agent-framework/react/e2e/playwright.config.ts",
        "skipInstall": true
      }
    },
    "test": {
      "executor": "@nx/vitest:test",
      "options": {
        "configFile": "cockpit/runtimes/microsoft-agent-framework/react/vite.config.mts"
      }
    },
    "lint": { "executor": "@nx/eslint:lint" },
    "build": {
      "executor": "nx:run-commands",
      "cache": false,
      "dependsOn": [
        { "target": "build", "projects": ["core", "content", "react"] },
        { "target": "react-ag-ui", "projects": ["website-runtime-preparation"] }
      ],
      "outputs": [
        "{workspaceRoot}/dist/cockpit/runtimes/microsoft-agent-framework/react"
      ],
      "options": {
        "command": "node scripts/react-cockpit/build.mjs runtimes-microsoft-agent-framework"
      }
    }
  }
}

Looking for something specific?