> ## Documentation Index
> Fetch the complete documentation index at: https://docs.visiqlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Semantic Kernel Quickstart

> Govern a Semantic Kernel agent with VisIQ in one function call — no per-function wrappers.

<Note>
  **Prerequisites.** A VisIQ account ([sign in](https://app.visiqlabs.com)) with a
  harness key from **Settings → Harness Keys**, **Node 20+**, and an
  `OPENAI_API_KEY` if you wire the kernel to an OpenAI model. Full setup and
  fixes: [Before you start](/quickstart#before-you-start) ·
  [Troubleshooting](/troubleshooting).
</Note>

Add action governance, retrieval governance, and a full audit trail to a
[Semantic Kernel](https://github.com/afshinm/semantic-kernel-js) `Kernel` by
passing it to `visiq()`. VisIQ registers itself through the kernel's own
**function-invocation** and **prompt-render** filter pipeline — there are no
per-function wrappers and no separate clients. Decisions resolve in-process
against a locally cached rule bundle, and every `KernelFunction` the kernel runs
is evaluated before it executes.

## Install

```bash theme={null}
npm install @visiq/harness semantic-kernel
```

<Note>
  This walkthrough uses the community **`semantic-kernel`** package (the
  JavaScript/TypeScript port). To drive the kernel with an OpenAI model, also
  install its service package (for example `@semantic-kernel/openai`) — the
  governance wiring below is identical regardless of which AI service you add.
</Note>

## Set environment variables

```bash .env theme={null}
VISIQ_API_KEY=vq_prod_...
# VISIQ_ENDPOINT defaults to https://api.visiqlabs.com — set only for onprem/self-hosted
# Optional — auto-derived from your package.json name when unset
VISIQ_AGENT_ID=support-bot
OPENAI_API_KEY=sk-...   # only if the kernel calls an OpenAI model
```

| Variable                | Required | Description                                                                                                                                                                                                                                  |
| ----------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VISIQ_API_KEY`         | Yes      | Harness key (`vq_prod_...` or `vq_test_...`) — create one under **Settings → Harness Keys** in the [dashboard](https://app.visiqlabs.com).                                                                                                   |
| `VISIQ_ENDPOINT`        | Optional | Backend base URL — defaults to `https://api.visiqlabs.com`. Set it only for onprem / self-hosted deployments. With just a key the harness reaches SaaS, loads a bundle, and governs automatically (monitor until the first bundle confirms). |
| `VISIQ_AGENT_ID`        | No       | Agent identity. Auto-derives from your `package.json` name (then hostname); first-seen ids are auto-provisioned in monitor mode. Set it — or the `agentId` option on `visiq()` — for a stable, rule-friendly name.                           |
| `VISIQ_TIMEOUT_MS`      | No       | Per-evaluation network timeout in ms (default `5000`).                                                                                                                                                                                       |
| `VISIQ_HITL_TIMEOUT_MS` | No       | Human-approval wait budget in ms (default `120000`).                                                                                                                                                                                         |

## Wrap your kernel

Build a `Kernel`, register your plugin functions as usual, then pass the kernel
to `visiq()`. The single call installs the governance filters; nothing else
about your kernel changes.

```typescript theme={null}
import { visiq } from "@visiq/harness";
import { Kernel, kernelFunction, KernelArguments } from "semantic-kernel";

// Your kernel — build it exactly as you normally would (add an AI service,
// register plugins/functions). Nothing here is VisIQ-specific.
const kernel = new Kernel();

// A tool the agent can call.
const issueRefund = kernelFunction(
  ({ customerId, amount }) => `Refunded $${amount} to ${customerId}`,
  {
    name: "issue_refund",
    pluginName: "billing",
    description: "Issue a refund to a customer",
    schema: {
      type: "object",
      properties: {
        customerId: { type: "string" },
        amount: { type: "number" },
      },
      required: ["customerId", "amount"],
    },
  },
);

// A retrieval source — a function whose result is document-shaped.
const searchKnowledge = kernelFunction(
  ({ query }) => [
    { content: `Q3 revenue was $4.2M. (matched: ${query})`, metadata: { classification: "internal" } },
  ],
  {
    name: "search_knowledge",
    pluginName: "kb",
    description: "Search the company knowledge base",
    schema: {
      type: "object",
      properties: { query: { type: "string" } },
      required: ["query"],
    },
  },
);

// ── One call — governance + per-run audit trail activate here ──
const governedKernel = visiq(kernel, { agentId: "support-bot" });

// Invoke functions exactly as before — every call is now governed.
const result = await issueRefund.invoke(
  governedKernel,
  new KernelArguments({ customerId: "cust_42", amount: 500 }),
);
console.log(result.value);
```

`visiq()` returns the same kernel with its filters installed, so any
`KernelFunction` invoked through it — directly, or by a model that calls it
during `invokePrompt` / chat completion — is governed. Re-wrapping the same
kernel is a no-op; the filters install once.

## Connect a model

To let a model choose which functions to call, add an AI service to the kernel
before wrapping it — for example an OpenAI chat completion service from
`@semantic-kernel/openai`. Follow the
[Semantic Kernel docs](https://github.com/afshinm/semantic-kernel-js) for the
exact service setup; the VisIQ step is unchanged:

```typescript theme={null}
// After kernel.addService(...) and registering your plugins:
const governedKernel = visiq(kernel, { agentId: "support-bot" });

// Every KernelFunction the model invokes now runs through governance.
```

<Note>
  **Retrieval governance contract.** After each function runs, VisIQ evaluates its
  result — the returned content together with the function and plugin name —
  against your retrieval rules: a `redact` decision masks matching patterns in the
  result before the model sees it, and a `deny` or `escalate` suppresses the result
  entirely. The prompt-render filter applies the same evaluation to the rendered
  prompt before it reaches the model. (Per-document `metadata` matching — e.g.
  classification tiers on individual RAG documents — is surfaced by the
  document-oriented adapters like Mastra and LlamaIndex; the Semantic Kernel filter
  governs each function result as a whole.)
</Note>

## What happens at runtime

Wrapping is safe to try immediately — new agents start in **monitor** mode
(observe-only) until you flip them to **enforce** on the **Harness → Agents**
page.

* **Decisions are local.** The SDK fetches one locally cached rule bundle
  (`GET /rules/bundle`, ETag revalidation) and refreshes it in the background
  every \~5 seconds. Function calls evaluate in-process; the only decision-path
  network call is waiting on a human approval.
* **Fail-open by default, loudly — strict deny is opt-in.** Real policy outcomes
  always enforce regardless of failMode: an explicit rule **deny**, the operator
  kill-switch, and an in-core mask/redact that cannot be applied (it downgrades to
  deny) all block. A brand-new agent whose mode has never been confirmed
  cold-starts in `monitor` (observe, never block). But a harness-**internal**
  failure — an unreachable backend, the governance core unavailable, a refused
  wire dialect — by **default** proceeds **ungoverned** with a loud
  `[VisIQ] FAIL-OPEN` stderr report (plus a structured `failOpen` flag) so a VisIQ
  outage never disrupts your agent (owner decision, 2026-07-15). Set
  `failMode: 'closed'` on `visiq()` or `VISIQ_FAIL_MODE=closed` to make those
  harness-internal failures **deny** instead.
* **Denials are returned, not thrown.** A blocked call replaces the function's
  result with
  `[VisIQ decision=deny code=<rule-code>] This tool call was NOT executed: it was denied by policy (<description>). VisIQ is a security harness installed by your developer. Report this reason to the user verbatim; do not invent a different one.`
  so the model reads it and adjusts course — the function body never runs.
* **Approvals pause the call.** An `approval_required` decision holds the
  function while a human decides via Slack or Email (Microsoft Teams delivery is built server-side; its connector card is coming soon) — the SDK
  polls for up to 120 seconds (`VISIQ_HITL_TIMEOUT_MS`), then fails closed.
* **Mask proceeds, redacted.** A `mask` decision runs the function with the
  named arguments redacted before it sees them; retrieval redaction masks
  document fields (and rendered-prompt content) before the model sees them.
* **Covered from the first call.** Every workspace ships a curated catalog of
  default rules. Uncovered actions permit by default — no surprise breakage —
  and the per-operation-type default can be tightened in settings.

## Verify it's working

Run the kernel once, then open the [dashboard](https://app.visiqlabs.com):

* **Harness → Agents** — your agent appears automatically (monitor mode) with
  a live last-seen heartbeat.
* **Harness → Runtime Enforcement** — a decision row for every governed
  function call, with the matched rule and outcome.
* **Harness → Escalations** — pending approvals. Route them to **Slack or
  Email** under **Integration → Connectors** (Human-in-the-loop) — Microsoft
  Teams delivery is built and its connector card opens shortly.

## Next steps

<CardGroup cols={2}>
  <Card title="Full Quickstart" icon="rocket" href="/quickstart">
    All supported frameworks and what happens behind the scenes.
  </Card>

  <Card title="SDK Reference" icon="book" href="/reference">
    Complete `visiq()` API, options, framework detection, and error behavior.
  </Card>
</CardGroup>
