> ## Documentation Index
> Fetch the complete documentation index at: https://tbd-6fc993ce-hypeship-webmcp-control-telemetry.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Human-in-the-loop Secure Credential Collection and Form Filling

> Collect credentials from a human, then fill browser forms without passing raw values to your agent

## What you need

* a `KERNEL_API_KEY`, set in the environment where your agent runs the cli. don't paste the key into its prompt.
* the [KERNEL cli](/reference/cli), with `vaults credentials` and `fill` support.

```bash theme={null}
npm install -g @onkernel/cli
kernel vaults list --limit 1 -o json
kernel vaults credentials --help
```

## Try it with your agent

copy this prompt into your coding agent. it uses hacker news as an example login form. your agent will navigate to hacker news, then ask you to securely enter your login in a collection form rather than sharing the raw username and password in the conversation. the agent can then fill and complete the login flow for you.

```text theme={null}
you are a personal assistant operating on my behalf. use the KERNEL cli for
this task, not SDK calls or direct HTTP requests.

verify access with `kernel vaults list --limit 1 -o json` and check
`kernel vaults credentials --help`. stop if credential commands aren't available.

1. create a new, uniquely named vault for this test, then create a browser with
   that vault attached. remember both identifiers so you can clean them up later.
2. navigate to https://news.ycombinator.com/login and inspect the login form's
   field definitions, not populated values. distinguish the login form from
   the create-account form. create a credential item with description
   "Hacker News", a required text username with sensitive:false, and a required
   password with sensitive:true. don't include credential values in the spec.
3. present the returned collection URL to me in this private conversation.
   don't open it in your browser. i will enter my credentials and tell you when
   i'm done. never ask me to paste my password into the conversation.
4. after i confirm, retrieve the item and require ready plus an advertised fill
   operation. invoke fill once with the browser session ID, exact login URL,
   and selectors that uniquely identify the login inputs. don't read, print,
   screenshot, or return the filled values. if fill fails, returns unknown, or
   loses its response, stop rather than retrying or using another fill path.
5. if fill completed, submit login once, verify the site's response, and tell me
   my karma. if login fails or needs more interaction, ask me what to do next.
   when we're finished, delete the test browser and only the vault created for
   this test. don't delete any pre-existing resources.
```

the collection url is a private bearer link: share it only with the intended user. the cli displays the link and value-presence metadata, not stored field values. the human enters the password in the collection form; the agent invokes fill by field name. see [fill's browser-access boundary](/vaults/fill) for what happens after values enter the page.

## 1. Create a vault per end-user

a vault groups one user's credentials. use an immutable name tied to that user in your application. the prompt above creates a temporary vault instead so you can delete the demo afterward.

all examples use the default project; use the same project for the vault and browser if you select a different one.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import Kernel from "@onkernel/sdk";

  const kernel = new Kernel();
  const vault = await kernel.vaults.upsert({ name: "user-12345" });
  ```

  ```python Python theme={null}
  from kernel import Kernel

  kernel = Kernel()
  vault = kernel.vaults.upsert(name="user-12345")
  ```

  ```bash CLI theme={null}
  VAULT_NAME="user-12345"
  kernel vaults create --name "$VAULT_NAME"
  ```
</CodeGroup>

## 2. Create a browser with the vault attached

attach the vault when you create the browser. the attachment can't change afterward and grants access to all items in that vault, including credentials added later.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const browser = await kernel.browsers.create({ vaults: [{ id: vault.id }] });
  ```

  ```python Python theme={null}
  browser = kernel.browsers.create(vaults=[{"id": vault.id}])
  ```

  ```bash CLI theme={null}
  kernel browsers create --vault "$VAULT_NAME" -o json
  read -r -p "paste the returned session_id: " BROWSER_ID
  ```
</CodeGroup>

use the returned browser session id for fill, not a browser name. the interactive `read` saves it for the shell examples below; an agent can retain the returned id directly.

## 3. Encounter a credential form

navigate to the login page and inspect the inputs before defining the credential item. hacker news has both login and create-account forms with the same input names. use selectors specific to the login form, and recheck them if the page changes.

create a field definition for each required input, leaving its value unset. use only the recognizable site name for `description`, and mark ordinary usernames or email addresses `sensitive: false`.

<CodeGroup>
  ```typescript TypeScript theme={null}
  await kernel.browsers.playwright.execute(browser.session_id, {
    code: "await page.goto('https://news.ycombinator.com/login'); return await page.title();",
  });
  const item = await kernel.vaults.items.upsert("hn-login", {
    id_or_name: vault.id,
    type: "credential",
    spec: {
      description: "Hacker News",
      fields: {
        username: { type: "text", required: true, sensitive: false },
        password: { type: "password", required: true, sensitive: true },
      },
    },
  });
  if (item.type !== "credential") throw new Error("expected a credential item");
  const collectionURL = item.action?.url;
  // Show collectionURL only to the intended user, not in general application logs.
  ```

  ```python Python theme={null}
  kernel.browsers.playwright.execute(
      browser.session_id,
      code="await page.goto('https://news.ycombinator.com/login'); return await page.title();",
  )
  item = kernel.vaults.items.upsert(
      "hn-login",
      id_or_name=vault.id,
      type="credential",
      spec={
          "description": "Hacker News",
          "fields": {
              "username": {"type": "text", "required": True, "sensitive": False},
              "password": {"type": "password", "required": True, "sensitive": True},
          },
      },
  )
  if item.type != "credential":
      raise RuntimeError("expected a credential item")
  collection_url = item.action.url if item.action else None
  # Show collection_url only to the intended user, not in general application logs.
  ```

  ```bash CLI theme={null}
  kernel browsers playwright execute "$BROWSER_ID" \
    "await page.goto('https://news.ycombinator.com/login'); return await page.title();"
  kernel vaults credentials create "$VAULT_NAME" hn-login --spec-file - <<'JSON'
  {
    "description": "Hacker News",
    "fields": {
      "username": {"type": "text", "required": true, "sensitive": false},
      "password": {"type": "password", "required": true, "sensitive": true}
    }
  }
  JSON
  ```
</CodeGroup>

the new item is `pending_collection` and returns a collection url. present it to the user and wait for their confirmation before continuing. in an application, render the url directly in the user's authenticated interface. the cli prompt above instead relays the link in a private conversation. don't open collection in the agent-controlled browser.

an existing ready item may omit the action. reuse it, or invoke the advertised `collect` operation to reopen the form without clearing values. see [credential collection](/vaults/credentials#collect-values-from-the-user) for expiry and editing behavior.

## 4. Fill credentials

retrieve the item after the user confirms collection. `ready` means required values exist, not that login succeeded. invoke only an advertised `fill` operation, with the exact current page url and unique input selectors. no credential values appear in the fill request.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const current = await kernel.vaults.items.retrieve(item.key, {
    id_or_name: vault.id,
    wait: 60,
  });
  if (current.id !== item.id || current.type !== "credential" ||
      current.state.status !== "ready" ||
      !current.available_operations.some((operation) => operation.type === "fill")) {
    throw new Error("credential is not ready to fill");
  }
  const result = await kernel.vaults.items.performOperation(item.key, {
    id_or_name: vault.id,
    type: "fill",
    browser_id: browser.session_id,
    page_url: "https://news.ycombinator.com/login",
    fields: [
      { field: "username", selector: "form:has(input[autocomplete='current-password']) input[name='acct']" },
      { field: "password", selector: "input[autocomplete='current-password']" },
    ],
  });
  if (result.type !== "fill" || result.status !== "completed") {
    throw new Error("stop and reconcile the fill outcome");
  }
  ```

  ```python Python theme={null}
  current = kernel.vaults.items.retrieve(item.key, id_or_name=vault.id, wait=60)
  if (current.id != item.id or current.type != "credential" or
          current.state.status != "ready" or
          not any(operation.type == "fill" for operation in current.available_operations)):
      raise RuntimeError("credential is not ready to fill")
  result = kernel.vaults.items.perform_operation(
      item.key,
      id_or_name=vault.id,
      type="fill",
      browser_id=browser.session_id,
      page_url="https://news.ycombinator.com/login",
      fields=[
          {"field": "username", "selector": "form:has(input[autocomplete='current-password']) input[name='acct']"},
          {"field": "password", "selector": "input[autocomplete='current-password']"},
      ],
  )
  if result.type != "fill" or result.status != "completed":
      raise RuntimeError("stop and reconcile the fill outcome")
  ```

  ```bash CLI theme={null}
  kernel vaults items get "$VAULT_NAME" hn-login --wait 60 -o json
  # Continue only if the same item is ready and advertises fill.
  kernel vaults items invoke "$VAULT_NAME" hn-login fill --spec-file - <<JSON
  {
    "browser_id": "$BROWSER_ID",
    "page_url": "https://news.ycombinator.com/login",
    "fields": [
      {"field": "username", "selector": "form:has(input[autocomplete='current-password']) input[name='acct']"},
      {"field": "password", "selector": "input[autocomplete='current-password']"}
    ]
  }
  JSON
  ```
</CodeGroup>

if the result is `completed`, the agent can submit login once and inspect the site's response. filling doesn't submit the form or confirm authentication. if the operation fails, returns `unknown`, or loses its response, stop instead of retrying. the [fill guide](/vaults/fill#handle-the-outcome) explains partial outcomes.

when the temporary demo is finished, delete its browser and vault. retain per-user vaults in your application according to your retention policy. for passwords on other sites, repeat the same flow with the fields and selectors you observe there; don't reuse a credential on a different destination without the user's authorization.
