> ## 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 — Microsoft's Python SDK or the npm JavaScript port.

<Note>
  **Which Semantic Kernel do you have?** Two different projects share this name.
  VisIQ governs both in one call — pick your runtime:

  | Your runtime                            | Package                                                                | One-call wrapper                          |
  | --------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------- |
  | **Microsoft Semantic Kernel — Python**  | PyPI [`semantic-kernel`](https://pypi.org/project/semantic-kernel/)    | `visiq.govern(kernel)`                    |
  | **Semantic Kernel JS** (community port) | npm [`semantic-kernel`](https://github.com/afshinm/semantic-kernel-js) | `visiq(kernel)`                           |
  | **Microsoft Semantic Kernel — Java**    | Maven                                                                  | **Not yet released** — see below          |
  | **Microsoft Semantic Kernel — .NET**    | NuGet                                                                  | **Not covered** — VisIQ ships no .NET SDK |

  Both supported runtimes register through the kernel's **own filter pipeline**,
  so there are no per-function wrappers: every `KernelFunction` on that kernel is
  governed, including the ones a model chooses during auto-invocation.

  Using Microsoft's **Java** SK? The adapter is built and its enforcement is
  proven, but it is **not yet released**: it does not currently match the rest of
  the VisIQ harness fleet (mask and redact outcomes block instead of running
  redacted), and we do not ship a harness that governs a narrower range than its
  siblings. Until it lands, wire the `com.visiqlabs:visiq-sdk` `gateAction` /
  `gateRetrieval` primitives into your own tool layer.
</Note>

## Python — Microsoft Semantic Kernel

```bash theme={null}
pip install visiq semantic-kernel
```

```python theme={null}
from semantic_kernel import Kernel
from semantic_kernel.functions import kernel_function
from visiq import govern

class Billing:
    @kernel_function(name="issue_refund", description="Issue a refund")
    def issue_refund(self, customer_id: str, amount: str) -> str:
        return f"Refunded ${amount} to {customer_id}"

kernel = Kernel()
kernel.add_plugin(Billing(), "billing")

# ── One call — every function on this kernel is now governed ──
govern(kernel, agent_id="support-bot")
```

`govern()` installs Semantic Kernel's `FUNCTION_INVOCATION`,
`AUTO_FUNCTION_INVOCATION` and `PROMPT_RENDERING` filters. A **deny** means the
decorated python method is never called at all — the block message is returned as
the function's result so the model reads why and adjusts course. A **mask** hands
the function only the redacted arguments. Set `retrieval_functions={"search"}` to
govern a RAG function's *result* through the retrieval facet instead:

```python theme={null}
govern(kernel, agent_id="support-bot", retrieval_functions={"search_knowledge"})
```

<Note>
  Governance decisions are local and synchronous, but they run **off your event
  loop** — a human-approval hold cannot freeze your agent's asyncio loop.
</Note>

## JavaScript — the community `semantic-kernel` port

The rest of this guide covers the npm JavaScript port. The governance model is
identical; only the API differs.

<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` — the
JavaScript port — 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) — not Microsoft's Python/.NET SDK of the same name.
  To drive the kernel with an OpenAI model, also install its service package (for
  example `@semantic-kernel/openai`, which is part of the same JS project) — 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>
