kcp-harness 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +201 -0
  2. package/dist/audit.d.ts +73 -0
  3. package/dist/audit.js +142 -0
  4. package/dist/budget-ledger.d.ts +85 -0
  5. package/dist/budget-ledger.js +100 -0
  6. package/dist/classifier.d.ts +35 -0
  7. package/dist/classifier.js +177 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +177 -0
  10. package/dist/config.d.ts +65 -0
  11. package/dist/config.js +91 -0
  12. package/dist/downstream.d.ts +50 -0
  13. package/dist/downstream.js +167 -0
  14. package/dist/governor.d.ts +34 -0
  15. package/dist/governor.js +201 -0
  16. package/dist/index.d.ts +12 -0
  17. package/dist/index.js +21 -0
  18. package/dist/integrations/claude-code.d.ts +2 -0
  19. package/dist/integrations/claude-code.js +95 -0
  20. package/dist/integrations/cline.d.ts +2 -0
  21. package/dist/integrations/cline.js +70 -0
  22. package/dist/integrations/continue.d.ts +2 -0
  23. package/dist/integrations/continue.js +39 -0
  24. package/dist/integrations/copilot.d.ts +2 -0
  25. package/dist/integrations/copilot.js +69 -0
  26. package/dist/integrations/crush.d.ts +2 -0
  27. package/dist/integrations/crush.js +48 -0
  28. package/dist/integrations/cursor.d.ts +2 -0
  29. package/dist/integrations/cursor.js +65 -0
  30. package/dist/integrations/generate.d.ts +13 -0
  31. package/dist/integrations/generate.js +60 -0
  32. package/dist/integrations/openclaw.d.ts +2 -0
  33. package/dist/integrations/openclaw.js +67 -0
  34. package/dist/integrations/types.d.ts +51 -0
  35. package/dist/integrations/types.js +78 -0
  36. package/dist/integrations/windsurf.d.ts +2 -0
  37. package/dist/integrations/windsurf.js +67 -0
  38. package/dist/kcp-bridge.d.ts +2 -0
  39. package/dist/kcp-bridge.js +85 -0
  40. package/dist/proxy.d.ts +37 -0
  41. package/dist/proxy.js +394 -0
  42. package/dist/session.d.ts +50 -0
  43. package/dist/session.js +82 -0
  44. package/dist/temporal-watch.d.ts +63 -0
  45. package/dist/temporal-watch.js +147 -0
  46. package/package.json +56 -0
@@ -0,0 +1,69 @@
1
+ // GitHub Copilot integration — .vscode/mcp.json + .github/copilot-instructions.md
2
+ //
3
+ // IMPORTANT: VS Code Copilot uses "servers" as the top-level key,
4
+ // NOT "mcpServers" like every other agent. MCP tools are only
5
+ // available in Agent mode.
6
+ import { harnessServerEntry, governedPathsBlock, manifestRef } from "./generate.js";
7
+ export function generateCopilot(options) {
8
+ const server = harnessServerEntry(options);
9
+ const manifest = manifestRef(options);
10
+ // VS Code uses "servers" (not "mcpServers")
11
+ const vscodeMcp = {
12
+ path: ".vscode/mcp.json",
13
+ content: JSON.stringify({
14
+ servers: {
15
+ "kcp-harness": {
16
+ type: "stdio",
17
+ ...server,
18
+ },
19
+ },
20
+ }, null, 2) + "\n",
21
+ commitToGit: true,
22
+ description: "VS Code MCP config — note: uses 'servers' key (not 'mcpServers'). Agent mode only.",
23
+ };
24
+ const instructions = {
25
+ path: ".github/copilot-instructions.md",
26
+ content: `# Knowledge Governance
27
+
28
+ This project uses the KCP Compliance Harness for deterministic knowledge governance.
29
+
30
+ ## Governed Paths
31
+
32
+ ${governedPathsBlock(options)}
33
+
34
+ These paths are governed by \`${manifest}\`. Use the kcp-harness MCP server to access them.
35
+
36
+ ## Rules
37
+
38
+ 1. Always call \`kcp_plan\` or \`kcp_load\` (via the kcp-harness MCP server) before reading governed files
39
+ 2. The harness enforces budget ceilings, temporal validity, and access control
40
+ 3. Use \`harness_status\` to check governance state
41
+ 4. Direct file reads of governed paths may be blocked by the governance layer
42
+
43
+ ## Available MCP Tools
44
+
45
+ - \`kcp_plan\` — deterministic load plan
46
+ - \`kcp_load\` — plan + load content
47
+ - \`kcp_trace\` — decision trace (13-gate cascade)
48
+ - \`harness_budget\` — session spend tracking
49
+ - \`harness_temporal_check\` — plan drift detection
50
+ `,
51
+ commitToGit: true,
52
+ description: "GitHub Copilot instructions — governance rules",
53
+ };
54
+ return {
55
+ agent: "copilot",
56
+ name: "GitHub Copilot",
57
+ files: [vscodeMcp, instructions],
58
+ instructions: `## GitHub Copilot Integration
59
+
60
+ **IMPORTANT:** VS Code Copilot uses \`"servers"\` as the top-level key in \`.vscode/mcp.json\` — NOT \`"mcpServers"\`. MCP tools are only available in **Agent mode** (not Ask or Edit mode).
61
+
62
+ **Setup:**
63
+ 1. Place \`.vscode/mcp.json\` in your project root
64
+ 2. Place \`.github/copilot-instructions.md\` in your project root
65
+ 3. In VS Code, switch Copilot Chat to Agent mode to access kcp-harness tools
66
+
67
+ For Copilot CLI, add to \`~/.copilot/mcp-config.json\` instead (uses \`mcpServers\` key + \`tools: ["*"]\`).`,
68
+ };
69
+ }
@@ -0,0 +1,2 @@
1
+ import type { IntegrationOutput, IntegrationOptions } from "./types.js";
2
+ export declare function generateCrush(options: IntegrationOptions): IntegrationOutput;
@@ -0,0 +1,48 @@
1
+ // Crush integration — MCP config + PrepareStep pattern documentation.
2
+ //
3
+ // Crush is a multi-model agent (30+ models) with native MCP support.
4
+ // The PrepareStep pattern lets Crush pre-load knowledge before the
5
+ // main task runs, ensuring governed knowledge is available in context.
6
+ import { harnessServerEntry, manifestRef } from "./generate.js";
7
+ export function generateCrush(options) {
8
+ const server = harnessServerEntry(options);
9
+ const manifest = manifestRef(options);
10
+ const crushJson = {
11
+ path: "crush.json",
12
+ content: JSON.stringify({
13
+ mcpServers: {
14
+ "kcp-harness": server,
15
+ },
16
+ prepareSteps: [
17
+ {
18
+ name: "load-knowledge",
19
+ description: "Pre-load governed knowledge via KCP harness",
20
+ tool: "kcp_plan",
21
+ arguments: {
22
+ task: "{{task}}",
23
+ manifest: manifest,
24
+ },
25
+ },
26
+ ],
27
+ }, null, 2) + "\n",
28
+ commitToGit: true,
29
+ description: "Crush MCP config with PrepareStep for knowledge pre-loading",
30
+ };
31
+ return {
32
+ agent: "crush",
33
+ name: "Crush",
34
+ files: [crushJson],
35
+ instructions: `## Crush Integration
36
+
37
+ **Setup:**
38
+ 1. Place \`crush.json\` in your project root
39
+ 2. Crush will auto-discover the kcp-harness MCP server
40
+
41
+ The \`prepareSteps\` configuration runs \`kcp_plan\` before each task, pre-loading the governance plan. This ensures:
42
+ - The harness has an approved plan before Crush accesses any files
43
+ - Plan-first mode kicks in for subsequent reads (fast path)
44
+ - Budget is tracked from the first tool call
45
+
46
+ The \`{{task}}\` placeholder is replaced with Crush's current task description.`,
47
+ };
48
+ }
@@ -0,0 +1,2 @@
1
+ import type { IntegrationOutput, IntegrationOptions } from "./types.js";
2
+ export declare function generateCursor(options: IntegrationOptions): IntegrationOutput;
@@ -0,0 +1,65 @@
1
+ // Cursor integration — .cursor/mcp.json + .cursor/rules/kcp-governance.mdc
2
+ import { harnessServerEntry, governedPathsBlock, manifestRef } from "./generate.js";
3
+ export function generateCursor(options) {
4
+ const server = harnessServerEntry(options);
5
+ const manifest = manifestRef(options);
6
+ const paths = options.paths ?? ["docs/", "src/"];
7
+ const mcpJson = {
8
+ path: ".cursor/mcp.json",
9
+ content: JSON.stringify({
10
+ mcpServers: {
11
+ "kcp-harness": server,
12
+ },
13
+ }, null, 2) + "\n",
14
+ commitToGit: true,
15
+ description: "Cursor MCP config — adds kcp-harness as governance proxy",
16
+ };
17
+ const rulesFile = {
18
+ path: ".cursor/rules/kcp-governance.mdc",
19
+ content: `---
20
+ globs: [${paths.map((p) => `"${p}**"`).join(", ")}]
21
+ alwaysApply: true
22
+ ---
23
+
24
+ # KCP Knowledge Governance
25
+
26
+ This project enforces deterministic knowledge governance via the KCP Compliance Harness.
27
+
28
+ ## Governed Paths
29
+
30
+ ${governedPathsBlock(options)}
31
+
32
+ These paths are governed by \`${manifest}\`. Access is controlled by the kcp-agent planner (13-gate cascade).
33
+
34
+ ## Required Workflow
35
+
36
+ 1. **Before reading governed files**, call \`kcp_plan\` or \`kcp_load\` via the kcp-harness MCP server
37
+ 2. The harness will classify your tool calls and route knowledge-navigation through the planner
38
+ 3. Direct file reads of governed paths will be blocked if no approved plan exists
39
+
40
+ ## Available Tools (via kcp-harness MCP server)
41
+
42
+ - \`kcp_plan\` — produce a load plan without loading content
43
+ - \`kcp_load\` — plan + load content of eligible units
44
+ - \`kcp_trace\` — see the 13-gate decision trace
45
+ - \`harness_status\` — check governance state
46
+ - \`harness_budget\` — check remaining budget
47
+ - \`harness_temporal_check\` — verify plan temporal validity
48
+ `,
49
+ commitToGit: true,
50
+ description: "Cursor rules — governance instructions scoped to governed paths",
51
+ };
52
+ return {
53
+ agent: "cursor",
54
+ name: "Cursor",
55
+ files: [mcpJson, rulesFile],
56
+ instructions: `## Cursor Integration
57
+
58
+ 1. Place \`.cursor/mcp.json\` in your project root — Cursor loads MCP servers from here
59
+ 2. Place \`.cursor/rules/kcp-governance.mdc\` — Cursor applies these rules when editing governed paths
60
+
61
+ **Setup:** Copy the generated files to your project root. Restart Cursor to pick up the MCP server.
62
+
63
+ The \`.mdc\` file uses glob-scoped rules so governance instructions only activate when working in governed directories.`,
64
+ };
65
+ }
@@ -0,0 +1,13 @@
1
+ import type { AgentTarget, IntegrationOutput, IntegrationOptions } from "./types.js";
2
+ /** Generate integration files for a specific agent. */
3
+ export declare function generate(agent: AgentTarget, options?: IntegrationOptions): IntegrationOutput;
4
+ /** Generate integration files for all agents. */
5
+ export declare function generateAll(options?: IntegrationOptions): IntegrationOutput[];
6
+ /** List all supported agent targets. */
7
+ export declare function listAgents(): AgentTarget[];
8
+ /** Build the MCP server entry for kcp-harness. */
9
+ export declare function harnessServerEntry(options: IntegrationOptions): Record<string, unknown>;
10
+ /** Build governed paths description for rules files. */
11
+ export declare function governedPathsBlock(options: IntegrationOptions): string;
12
+ /** Build manifest reference for rules files. */
13
+ export declare function manifestRef(options: IntegrationOptions): string;
@@ -0,0 +1,60 @@
1
+ // Integration generator — produce agent-specific configs from harness config.
2
+ //
3
+ // Each agent has its own MCP config format, rules file, and setup pattern.
4
+ // The generator takes a harness config and produces the files needed to
5
+ // integrate the harness with a specific agent.
6
+ import { generateClaudeCode } from "./claude-code.js";
7
+ import { generateCursor } from "./cursor.js";
8
+ import { generateWindsurf } from "./windsurf.js";
9
+ import { generateCline } from "./cline.js";
10
+ import { generateContinue } from "./continue.js";
11
+ import { generateCopilot } from "./copilot.js";
12
+ import { generateCrush } from "./crush.js";
13
+ import { generateOpenClaw } from "./openclaw.js";
14
+ const GENERATORS = {
15
+ "claude-code": generateClaudeCode,
16
+ cursor: generateCursor,
17
+ windsurf: generateWindsurf,
18
+ cline: generateCline,
19
+ continue: generateContinue,
20
+ copilot: generateCopilot,
21
+ "copilot-cli": generateCopilot, // Same format, different path
22
+ crush: generateCrush,
23
+ openclaw: generateOpenClaw,
24
+ };
25
+ /** Generate integration files for a specific agent. */
26
+ export function generate(agent, options = {}) {
27
+ const gen = GENERATORS[agent];
28
+ if (!gen)
29
+ throw new Error(`unsupported agent: ${agent}`);
30
+ return gen(options);
31
+ }
32
+ /** Generate integration files for all agents. */
33
+ export function generateAll(options = {}) {
34
+ return Object.keys(GENERATORS).map((agent) => generate(agent, options));
35
+ }
36
+ /** List all supported agent targets. */
37
+ export function listAgents() {
38
+ return Object.keys(GENERATORS);
39
+ }
40
+ // -- Shared helpers for generators ------------------------------------------
41
+ /** Build the MCP server entry for kcp-harness. */
42
+ export function harnessServerEntry(options) {
43
+ return {
44
+ command: options.harnessCommand ?? "npx",
45
+ args: options.harnessArgs ?? [
46
+ "kcp-harness",
47
+ "serve",
48
+ ...(options.harnessConfig ? ["--config", options.harnessConfig] : []),
49
+ ],
50
+ };
51
+ }
52
+ /** Build governed paths description for rules files. */
53
+ export function governedPathsBlock(options) {
54
+ const paths = options.paths ?? ["docs/", "src/"];
55
+ return paths.map((p) => ` - \`${p}\``).join("\n");
56
+ }
57
+ /** Build manifest reference for rules files. */
58
+ export function manifestRef(options) {
59
+ return options.manifest ?? "./knowledge.yaml";
60
+ }
@@ -0,0 +1,2 @@
1
+ import type { IntegrationOutput, IntegrationOptions } from "./types.js";
2
+ export declare function generateOpenClaw(options: IntegrationOptions): IntegrationOutput;
@@ -0,0 +1,67 @@
1
+ // OpenClaw integration — openclaw.json + plugin hooks documentation.
2
+ //
3
+ // OpenClaw is an open-source multi-channel agent with plugin hooks:
4
+ // - before_prompt_build: inject kcp-loaded knowledge into prompt
5
+ // - before_agent_finalize: post-synthesis claim verification
6
+ //
7
+ // OpenClaw's MCP support uses the standard mcpServers key.
8
+ import { harnessServerEntry, manifestRef } from "./generate.js";
9
+ export function generateOpenClaw(options) {
10
+ const server = harnessServerEntry(options);
11
+ const manifest = manifestRef(options);
12
+ const openclawJson = {
13
+ path: "openclaw.json",
14
+ content: JSON.stringify({
15
+ mcpServers: {
16
+ "kcp-harness": server,
17
+ },
18
+ plugins: {
19
+ "kcp-governance": {
20
+ hooks: {
21
+ before_prompt_build: {
22
+ tool: "kcp_load",
23
+ arguments: {
24
+ task: "{{task}}",
25
+ manifest: manifest,
26
+ },
27
+ description: "Load governed knowledge via KCP harness before prompt assembly",
28
+ },
29
+ before_agent_finalize: {
30
+ tool: "kcp_trace",
31
+ arguments: {
32
+ task: "{{task}}",
33
+ manifest: manifest,
34
+ },
35
+ description: "Run decision trace for audit trail before finalizing agent response",
36
+ },
37
+ },
38
+ },
39
+ },
40
+ }, null, 2) + "\n",
41
+ commitToGit: true,
42
+ description: "OpenClaw MCP config with plugin hooks for knowledge governance",
43
+ };
44
+ return {
45
+ agent: "openclaw",
46
+ name: "OpenClaw",
47
+ files: [openclawJson],
48
+ instructions: `## OpenClaw Integration
49
+
50
+ **Setup:**
51
+ 1. Place \`openclaw.json\` in your project root
52
+ 2. OpenClaw will auto-discover the kcp-harness MCP server
53
+
54
+ **Plugin Hooks:**
55
+ - \`before_prompt_build\` — calls \`kcp_load\` to inject governed knowledge before the prompt is assembled. This ensures the model sees governed knowledge as part of its context, not as a separate tool call.
56
+ - \`before_agent_finalize\` — calls \`kcp_trace\` after the agent produces its response, creating a decision trace for the audit log.
57
+
58
+ The \`{{task}}\` placeholder is replaced with OpenClaw's current task description.
59
+
60
+ **Available MCP Tools:**
61
+ - \`kcp_plan\` — deterministic load plan
62
+ - \`kcp_load\` — plan + load content
63
+ - \`kcp_trace\` — 13-gate decision trace
64
+ - \`harness_budget\` — session spend tracking
65
+ - \`harness_temporal_check\` — plan drift detection`,
66
+ };
67
+ }
@@ -0,0 +1,51 @@
1
+ /** Supported agent targets for integration generation. */
2
+ export type AgentTarget = "claude-code" | "cursor" | "windsurf" | "cline" | "continue" | "copilot" | "copilot-cli" | "crush" | "openclaw";
3
+ /** Integration output — generated files for a specific agent. */
4
+ export interface IntegrationOutput {
5
+ /** Which agent this integration is for. */
6
+ agent: AgentTarget;
7
+ /** Display name. */
8
+ name: string;
9
+ /** Files to write (path → content). */
10
+ files: IntegrationFile[];
11
+ /** Human-readable setup instructions (markdown). */
12
+ instructions: string;
13
+ }
14
+ /** A file to write as part of the integration. */
15
+ export interface IntegrationFile {
16
+ /** Relative path from project root. */
17
+ path: string;
18
+ /** File content. */
19
+ content: string;
20
+ /** Whether this file should be committed to git. */
21
+ commitToGit: boolean;
22
+ /** Description of what this file does. */
23
+ description: string;
24
+ }
25
+ /** Options for generating an integration. */
26
+ export interface IntegrationOptions {
27
+ /** Path to the harness YAML config. */
28
+ harnessConfig?: string;
29
+ /** Governed knowledge.yaml path. */
30
+ manifest?: string;
31
+ /** Governed path prefixes. */
32
+ paths?: string[];
33
+ /** Project root (for relative path resolution). */
34
+ projectRoot?: string;
35
+ /** Custom harness command (default: npx kcp-harness serve). */
36
+ harnessCommand?: string;
37
+ /** Custom harness args. */
38
+ harnessArgs?: string[];
39
+ }
40
+ /** Agent metadata. */
41
+ export interface AgentInfo {
42
+ target: AgentTarget;
43
+ name: string;
44
+ mcpSupport: boolean;
45
+ configFile: string;
46
+ rulesFile?: string;
47
+ topLevelKey: string;
48
+ notes?: string;
49
+ }
50
+ /** Registry of all supported agents. */
51
+ export declare const AGENTS: Record<AgentTarget, AgentInfo>;
@@ -0,0 +1,78 @@
1
+ // Integration types — shared across all agent integration packages.
2
+ /** Registry of all supported agents. */
3
+ export const AGENTS = {
4
+ "claude-code": {
5
+ target: "claude-code",
6
+ name: "Claude Code",
7
+ mcpSupport: true,
8
+ configFile: ".mcp.json",
9
+ rulesFile: "CLAUDE.md",
10
+ topLevelKey: "mcpServers",
11
+ notes: "First-class MCP + hooks (PreToolUse)",
12
+ },
13
+ cursor: {
14
+ target: "cursor",
15
+ name: "Cursor",
16
+ mcpSupport: true,
17
+ configFile: ".cursor/mcp.json",
18
+ rulesFile: ".cursor/rules/kcp-governance.mdc",
19
+ topLevelKey: "mcpServers",
20
+ },
21
+ windsurf: {
22
+ target: "windsurf",
23
+ name: "Windsurf",
24
+ mcpSupport: true,
25
+ configFile: "~/.codeium/windsurf/mcp_config.json",
26
+ rulesFile: ".windsurfrules",
27
+ topLevelKey: "mcpServers",
28
+ notes: "Global config only (no per-project MCP file)",
29
+ },
30
+ cline: {
31
+ target: "cline",
32
+ name: "Cline",
33
+ mcpSupport: true,
34
+ configFile: "cline_mcp_settings.json",
35
+ rulesFile: ".clinerules",
36
+ topLevelKey: "mcpServers",
37
+ notes: "Config managed via VS Code extension UI",
38
+ },
39
+ continue: {
40
+ target: "continue",
41
+ name: "Continue",
42
+ mcpSupport: true,
43
+ configFile: ".continue/mcpServers/kcp-harness.yaml",
44
+ topLevelKey: "mcpServers",
45
+ },
46
+ copilot: {
47
+ target: "copilot",
48
+ name: "GitHub Copilot",
49
+ mcpSupport: true,
50
+ configFile: ".vscode/mcp.json",
51
+ rulesFile: ".github/copilot-instructions.md",
52
+ topLevelKey: "servers",
53
+ notes: "Uses 'servers' not 'mcpServers'. Agent mode only.",
54
+ },
55
+ "copilot-cli": {
56
+ target: "copilot-cli",
57
+ name: "GitHub Copilot CLI",
58
+ mcpSupport: true,
59
+ configFile: "~/.copilot/mcp-config.json",
60
+ topLevelKey: "mcpServers",
61
+ },
62
+ crush: {
63
+ target: "crush",
64
+ name: "Crush",
65
+ mcpSupport: true,
66
+ configFile: "crush.json",
67
+ topLevelKey: "mcpServers",
68
+ notes: "PrepareStep pattern for knowledge pre-loading",
69
+ },
70
+ openclaw: {
71
+ target: "openclaw",
72
+ name: "OpenClaw",
73
+ mcpSupport: true,
74
+ configFile: "openclaw.json",
75
+ topLevelKey: "mcpServers",
76
+ notes: "Plugin hooks: before_prompt_build, before_agent_finalize",
77
+ },
78
+ };
@@ -0,0 +1,2 @@
1
+ import type { IntegrationOutput, IntegrationOptions } from "./types.js";
2
+ export declare function generateWindsurf(options: IntegrationOptions): IntegrationOutput;
@@ -0,0 +1,67 @@
1
+ // Windsurf (Codeium) integration — global MCP config + .windsurfrules
2
+ import { harnessServerEntry, governedPathsBlock, manifestRef } from "./generate.js";
3
+ export function generateWindsurf(options) {
4
+ const server = harnessServerEntry(options);
5
+ const manifest = manifestRef(options);
6
+ // Windsurf uses a GLOBAL config — we generate the snippet to add
7
+ const mcpSnippet = {
8
+ path: "windsurf-mcp-snippet.json",
9
+ content: JSON.stringify({
10
+ "kcp-harness": server,
11
+ }, null, 2) + "\n",
12
+ commitToGit: false,
13
+ description: "MCP snippet to add to ~/.codeium/windsurf/mcp_config.json (global config)",
14
+ };
15
+ const rulesFile = {
16
+ path: ".windsurfrules",
17
+ content: `# KCP Knowledge Governance
18
+
19
+ This project uses the KCP Compliance Harness for deterministic knowledge access control.
20
+
21
+ ## Governed Paths
22
+
23
+ ${governedPathsBlock(options)}
24
+
25
+ These paths are governed by \`${manifest}\`.
26
+
27
+ ## Rules
28
+
29
+ 1. Always call \`kcp_plan\` or \`kcp_load\` (via the kcp-harness MCP server) before reading governed files
30
+ 2. The harness intercepts tool calls and routes knowledge access through the kcp-agent planner
31
+ 3. Budget and temporal governance are enforced automatically
32
+ 4. Use \`harness_status\` to check the current governance state
33
+
34
+ ## Available MCP Tools
35
+
36
+ - \`kcp_plan\` — deterministic load plan (no content)
37
+ - \`kcp_load\` — plan + load eligible unit content
38
+ - \`kcp_trace\` — 13-gate decision trace
39
+ - \`harness_budget\` — session spend tracking
40
+ - \`harness_temporal_check\` — plan drift detection
41
+ `,
42
+ commitToGit: true,
43
+ description: "Windsurf rules — governance instructions for Cascade",
44
+ };
45
+ return {
46
+ agent: "windsurf",
47
+ name: "Windsurf",
48
+ files: [mcpSnippet, rulesFile],
49
+ instructions: `## Windsurf Integration
50
+
51
+ Windsurf uses a **global** MCP config (not per-project).
52
+
53
+ **Setup:**
54
+ 1. Add the kcp-harness entry to \`~/.codeium/windsurf/mcp_config.json\`:
55
+ \`\`\`json
56
+ {
57
+ "mcpServers": {
58
+ "kcp-harness": { ... } // ← merge from windsurf-mcp-snippet.json
59
+ }
60
+ }
61
+ \`\`\`
62
+ 2. Place \`.windsurfrules\` in your project root
63
+ 3. Restart Windsurf
64
+
65
+ The \`.windsurfrules\` file instructs Cascade to use kcp_load for governed knowledge access.`,
66
+ };
67
+ }
@@ -0,0 +1,2 @@
1
+ /** Call a KCP tool and return the result as a JSON string. */
2
+ export declare function callKcpTool(name: string, args: Record<string, unknown>): Promise<string>;
@@ -0,0 +1,85 @@
1
+ // KCP bridge — call kcp-agent's planner functions from the harness.
2
+ //
3
+ // Instead of importing kcp-agent's internal MCP handler (which isn't part
4
+ // of the public API), we use the exported planner functions directly.
5
+ // This gives us the same results with type safety and a stable API surface.
6
+ import { planTree, plans, loadPlannedUnits, loadManifest, trace as traceDecision, validateLocation, dedupeLoaded, } from "kcp-agent";
7
+ /** Accept a JSON array or a comma-separated string — MCP callers send both. */
8
+ function toList(v) {
9
+ if (Array.isArray(v))
10
+ return v.map(String);
11
+ if (typeof v === "string")
12
+ return v.split(",").map((s) => s.trim()).filter(Boolean);
13
+ return undefined;
14
+ }
15
+ /** Map MCP-style arguments to kcp-agent's FollowOptions. */
16
+ function toFollowOptions(args) {
17
+ const methods = toList(args["methods"]);
18
+ const credentials = toList(args["credentials"]);
19
+ const planOptions = {
20
+ env: args["env"] === undefined ? undefined : String(args["env"]),
21
+ asOf: args["as_of"] === undefined ? undefined : String(args["as_of"]),
22
+ maxUnits: args["max_units"] === undefined ? undefined : Number(args["max_units"]),
23
+ strict: args["strict"] === true,
24
+ budget: args["budget"] === undefined
25
+ ? undefined
26
+ : { amount: Number(args["budget"]), currency: args["currency"] === undefined ? undefined : String(args["currency"]) },
27
+ contextBudget: args["context_budget"] === undefined ? undefined : Number(args["context_budget"]),
28
+ capabilities: {
29
+ ...(args["role"] === undefined ? {} : { role: String(args["role"]) }),
30
+ ...(methods ? { paymentMethods: methods } : {}),
31
+ ...(credentials ? { credentials } : {}),
32
+ ...(args["attest"] === undefined ? {} : { attestationProvider: String(args["attest"]) }),
33
+ },
34
+ };
35
+ return {
36
+ planOptions,
37
+ maxDepth: args["follow"] === true ? (args["max_depth"] === undefined ? 1 : Number(args["max_depth"])) : 0,
38
+ maxNodes: args["max_nodes"] === undefined ? undefined : Number(args["max_nodes"]),
39
+ fetchGuard: { allowPrivate: args["allow_private_hosts"] === true },
40
+ };
41
+ }
42
+ /** Call a KCP tool and return the result as a JSON string. */
43
+ export async function callKcpTool(name, args) {
44
+ switch (name) {
45
+ case "kcp_plan": {
46
+ const tree = await planTree(String(args["manifest"] ?? ""), String(args["task"] ?? ""), toFollowOptions(args));
47
+ if (tree.error)
48
+ throw new Error(`${tree.location}: ${tree.error}`);
49
+ return JSON.stringify(tree, null, 2);
50
+ }
51
+ case "kcp_load": {
52
+ const follow = toFollowOptions(args);
53
+ const tree = await planTree(String(args["manifest"] ?? ""), String(args["task"] ?? ""), follow);
54
+ if (tree.error)
55
+ throw new Error(`${tree.location}: ${tree.error}`);
56
+ const loaded = [];
57
+ const unavailable = [];
58
+ for (const p of plans(tree)) {
59
+ const r = await loadPlannedUnits(p, follow.fetchGuard);
60
+ loaded.push(...r.loaded);
61
+ unavailable.push(...r.unavailable);
62
+ }
63
+ const { units, deduped, bytesSaved } = dedupeLoaded(loaded, args["known"]);
64
+ return JSON.stringify({ plan: tree, units, unavailable, deduped, bytesSaved }, null, 2);
65
+ }
66
+ case "kcp_trace": {
67
+ const follow = toFollowOptions(args);
68
+ const manifest = await loadManifest(String(args["manifest"] ?? ""), follow.fetchGuard);
69
+ const t = traceDecision(manifest, String(args["task"] ?? ""), follow.planOptions);
70
+ return JSON.stringify(t, null, 2);
71
+ }
72
+ case "kcp_validate": {
73
+ const guard = { allowPrivate: args["allow_private_hosts"] === true };
74
+ const report = await validateLocation(String(args["manifest"] ?? ""), guard);
75
+ return JSON.stringify(report, null, 2);
76
+ }
77
+ case "kcp_replay": {
78
+ // Replay requires importing the replay function — use dynamic import
79
+ // to keep the bridge lightweight for non-replay use cases.
80
+ throw new Error("kcp_replay not yet bridged — call kcp-agent directly");
81
+ }
82
+ default:
83
+ throw new Error(`unknown KCP tool: ${name}`);
84
+ }
85
+ }
@@ -0,0 +1,37 @@
1
+ import type { HarnessConfig } from "./config.js";
2
+ import { type AuditWriter } from "./audit.js";
3
+ import { type SessionState } from "./session.js";
4
+ interface JsonRpcRequest {
5
+ jsonrpc?: string;
6
+ id?: number | string | null;
7
+ method?: string;
8
+ params?: Record<string, unknown>;
9
+ }
10
+ export interface ProxyOptions {
11
+ config: HarnessConfig;
12
+ audit?: AuditWriter;
13
+ }
14
+ export declare class HarnessProxy {
15
+ private readonly config;
16
+ private readonly downstream;
17
+ private readonly audit;
18
+ private readonly session;
19
+ constructor(options: ProxyOptions);
20
+ /** Start the proxy: spawn downstream servers and begin serving. */
21
+ start(): Promise<void>;
22
+ /** Shut down the proxy and all downstream connections. */
23
+ stop(): Promise<void>;
24
+ /** Handle one JSON-RPC message from the agent. */
25
+ handleMessage(msg: JsonRpcRequest): Promise<object | null>;
26
+ /** List all available tools (downstream + harness + KCP). */
27
+ private listTools;
28
+ /** Handle a tool call through the governance pipeline. */
29
+ private handleToolCall;
30
+ /** Handle harness-internal tools. */
31
+ private handleHarnessTool;
32
+ /** Expose session for testing. */
33
+ getSession(): SessionState;
34
+ }
35
+ /** Serve the harness proxy over stdio until stdin closes. */
36
+ export declare function serveProxy(config: HarnessConfig): Promise<void>;
37
+ export {};