@sweny-ai/core 0.1.132 → 0.1.134

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.
@@ -0,0 +1,29 @@
1
+ /**
2
+ * PR billboard: the markdown a CI runner posts as one sticky comment on a
3
+ * pull request. Pure and runner-agnostic; the CLI writes it with
4
+ * `--comment-file`, and any runner (the GitHub Action, GitLab, a script) posts it.
5
+ *
6
+ * METADATA ONLY. Node names, statuses, counts, and durations. Never prompts,
7
+ * tool inputs, node outputs, errors, or model prose.
8
+ */
9
+ import type { ExecutionTrace, NodeResult, Workflow } from "../types.js";
10
+ import { type RunSummary } from "./run-output.js";
11
+ export declare const RUN_COMMENT_FOOTER = "Run with [SWEny](https://sweny.ai): `npx @sweny-ai/core new`";
12
+ export interface RunCommentOptions {
13
+ trace?: ExecutionTrace;
14
+ /** Per-node wall-clock duration in ms, keyed by node id. Missing entries render as "-". */
15
+ durationsMs?: Record<string, number>;
16
+ }
17
+ /** Hidden marker the poster searches for to update the comment in place. */
18
+ export declare function runCommentMarker(workflowId: string): string;
19
+ export declare function formatRunComment(workflow: Workflow, results: Map<string, NodeResult>, summary: RunSummary, opts?: RunCommentOptions): string;
20
+ /**
21
+ * Minimal comment for a run that crashed before producing node results: no DAG
22
+ * and no error text (thrown messages can embed model prose). Replaces any stale
23
+ * success comment for the same workflow.
24
+ */
25
+ export declare function formatCrashComment(workflow: Workflow, summary: RunSummary): string;
26
+ /** Write the comment markdown to `file`. Never throws; returns true only when written. */
27
+ export declare function writeRunComment(file: string, workflow: Workflow, results: Map<string, NodeResult>, summary: RunSummary, opts?: RunCommentOptions & {
28
+ crashed?: boolean;
29
+ }): boolean;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * PR billboard: the markdown a CI runner posts as one sticky comment on a
3
+ * pull request. Pure and runner-agnostic; the CLI writes it with
4
+ * `--comment-file`, and any runner (the GitHub Action, GitLab, a script) posts it.
5
+ *
6
+ * METADATA ONLY. Node names, statuses, counts, and durations. Never prompts,
7
+ * tool inputs, node outputs, errors, or model prose.
8
+ */
9
+ import fs from "node:fs";
10
+ import path from "node:path";
11
+ import { toMermaidBlock } from "../mermaid.js";
12
+ import { STEP_SUMMARY_CLASS_DEFS, formatReceipt, formatReceiptDuration, nodeStates, } from "./run-output.js";
13
+ export const RUN_COMMENT_FOOTER = "Run with [SWEny](https://sweny.ai): `npx @sweny-ai/core new`";
14
+ /** Hidden marker the poster searches for to update the comment in place. */
15
+ export function runCommentMarker(workflowId) {
16
+ const safe = workflowId.replace(/[^A-Za-z0-9._-]+/g, "_").replace(/-{2,}/g, "-") || "workflow";
17
+ return `<!-- sweny-run-comment:${safe} -->`;
18
+ }
19
+ /** Table-cell-safe text: no pipes, newlines, or backticks. */
20
+ function cell(text) {
21
+ return text.replace(/\s+/g, " ").replace(/\|/g, "\\|").replace(/`/g, "'").trim();
22
+ }
23
+ const STATUS_LABEL = { success: "✅ success", failed: "❌ failed", skipped: "⏭️ skipped" };
24
+ export function formatRunComment(workflow, results, summary, opts = {}) {
25
+ const state = nodeStates(results);
26
+ const rows = [...results].map(([id, r]) => {
27
+ const name = cell(workflow.nodes[id]?.name ?? id);
28
+ const ms = opts.durationsMs?.[id];
29
+ const dur = typeof ms === "number" && Number.isFinite(ms) ? formatReceiptDuration(ms) : "-";
30
+ return `| ${name} | ${STATUS_LABEL[r.status]} | ${dur} |`;
31
+ });
32
+ return [
33
+ runCommentMarker(workflow.id),
34
+ `## ${summary.ok ? "✅" : "❌"} ${cell(workflow.name)}`,
35
+ "",
36
+ `\`${formatReceipt(summary)}\``,
37
+ "",
38
+ toMermaidBlock(workflow, { state, trace: opts.trace, classDefs: STEP_SUMMARY_CLASS_DEFS }),
39
+ "",
40
+ "<details>",
41
+ "<summary>Nodes</summary>",
42
+ "",
43
+ "| Node | Status | Duration |",
44
+ "| --- | --- | --- |",
45
+ ...rows,
46
+ "",
47
+ "</details>",
48
+ "",
49
+ `<sub>${RUN_COMMENT_FOOTER}</sub>`,
50
+ "",
51
+ ].join("\n");
52
+ }
53
+ /**
54
+ * Minimal comment for a run that crashed before producing node results: no DAG
55
+ * and no error text (thrown messages can embed model prose). Replaces any stale
56
+ * success comment for the same workflow.
57
+ */
58
+ export function formatCrashComment(workflow, summary) {
59
+ return [
60
+ runCommentMarker(workflow.id),
61
+ `## ❌ ${cell(workflow.name)}`,
62
+ "",
63
+ `\`${formatReceipt(summary)} · crashed\``,
64
+ "",
65
+ "The run stopped before finishing. See the job log for details.",
66
+ "",
67
+ `<sub>${RUN_COMMENT_FOOTER}</sub>`,
68
+ "",
69
+ ].join("\n");
70
+ }
71
+ /** Write the comment markdown to `file`. Never throws; returns true only when written. */
72
+ export function writeRunComment(file, workflow, results, summary, opts = {}) {
73
+ try {
74
+ fs.mkdirSync(path.dirname(file), { recursive: true });
75
+ fs.writeFileSync(file, opts.crashed ? formatCrashComment(workflow, summary) : formatRunComment(workflow, results, summary, opts));
76
+ return true;
77
+ }
78
+ catch (err) {
79
+ process.stderr.write(` ⚠ could not write comment file: ${err instanceof Error ? err.message : err}\n`);
80
+ return false;
81
+ }
82
+ }
package/dist/cli/e2e.js CHANGED
@@ -10,7 +10,7 @@ import * as p from "@clack/prompts";
10
10
  import chalk from "chalk";
11
11
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
12
12
  import { execute } from "../executor.js";
13
- import { ClaudeClient } from "../claude.js";
13
+ import { createHarness } from "../harness/index.js";
14
14
  import { createSkillMap } from "../skills/index.js";
15
15
  import { configuredSkills } from "../skills/custom-loader.js";
16
16
  import { consoleLogger } from "../types.js";
@@ -906,7 +906,7 @@ export async function runE2eRun(options) {
906
906
  }
907
907
  // Build skills + Claude client
908
908
  const skills = createSkillMap(configuredSkills(process.env, cwd));
909
- const claude = new ClaudeClient({
909
+ const claude = createHarness("claude-code", {
910
910
  maxTurns: 80,
911
911
  cwd,
912
912
  logger: consoleLogger,
@@ -5,6 +5,7 @@ export declare function workflowRunAction(file: string | undefined, options: Rec
5
5
  json?: boolean;
6
6
  stream?: boolean;
7
7
  mermaid?: boolean;
8
+ commentFile?: string;
8
9
  verbose?: boolean;
9
10
  timeout?: string;
10
11
  maxSteps?: string;
package/dist/cli/main.js CHANGED
@@ -9,7 +9,7 @@ import chalk from "chalk";
9
9
  import { execute } from "../executor.js";
10
10
  import { triageWorkflow, implementWorkflow, seedContentWorkflow } from "../workflows/index.js";
11
11
  import { consoleLogger } from "../types.js";
12
- import { ClaudeClient } from "../claude.js";
12
+ import { createHarness } from "../harness/index.js";
13
13
  import { builtinSkills, createSkillMap, validateWorkflowSkills } from "../skills/index.js";
14
14
  import { formatMissingSkillLines, skillEnvWarnings } from "./skill-env.js";
15
15
  import { configuredSkills, configuredSkillsWithDiagnostics } from "../skills/custom-loader.js";
@@ -32,6 +32,7 @@ import { registerRunsCommand } from "./runs.js";
32
32
  import { runE2eRun, runWithWallClockBudget, DEFAULT_WORKFLOW_TIMEOUT_MS } from "./e2e.js";
33
33
  import { createVerboseToolObserver } from "./verbose-observer.js";
34
34
  import { createRunLogger, renderReceiptLine, summarizeRun, writeStepSummary, WORKFLOW_RUN_DESCRIPTION, WORKFLOW_RUN_OPTIONS, } from "./run-output.js";
35
+ import { writeRunComment } from "./comment-output.js";
35
36
  import { registerTriageCommand, registerImplementCommand, parseCliInputs, validateInputs, validateWarnings, parsePositiveInt, } from "./config.js";
36
37
  import { c, formatBanner, getStepDetails, formatStepLine, formatDagResultHuman, formatDagResultMarkdown, formatResultJson, formatValidationErrors, formatCrashError, formatCheckResults, } from "./output.js";
37
38
  import { checkProviderConnectivity, detectClaudeCodeLogin, discoverWorkflowSkillIds, validateCheckInputs, } from "./check.js";
@@ -202,7 +203,7 @@ triageCmd.action(async (options) => {
202
203
  const skills = createSkillMap(triageSkillDiscovery.skills);
203
204
  const mcpAutoConfig = buildMcpAutoConfig(config);
204
205
  const mcpServers = buildAutoMcpServers(mcpAutoConfig);
205
- const claude = new ClaudeClient({
206
+ const claude = createHarness("claude-code", {
206
207
  maxTurns: config.maxInvestigateTurns || 50,
207
208
  cwd: process.cwd(),
208
209
  logger: consoleLogger,
@@ -512,7 +513,7 @@ implementCmd.action(async (issueId, options) => {
512
513
  const skills = createSkillMap(implementSkillDiscovery.skills);
513
514
  const mcpAutoConfig = buildMcpAutoConfig(config);
514
515
  const mcpServers = buildAutoMcpServers(mcpAutoConfig);
515
- const claude = new ClaudeClient({
516
+ const claude = createHarness("claude-code", {
516
517
  maxTurns: config.maxImplementTurns || 40,
517
518
  cwd: process.cwd(),
518
519
  logger: consoleLogger,
@@ -756,7 +757,7 @@ export async function workflowRunAction(file, options) {
756
757
  });
757
758
  // Raw [info]/[debug] lines are verbose-only; warnings are rendered (#383).
758
759
  const runLogger = createRunLogger({ verbose: Boolean(options.verbose), tty: isTTY });
759
- const claude = new ClaudeClient({
760
+ const claude = createHarness("claude-code", {
760
761
  maxTurns: config.maxInvestigateTurns || 50,
761
762
  cwd: process.cwd(),
762
763
  logger: runLogger,
@@ -948,6 +949,14 @@ export async function workflowRunAction(file, options) {
948
949
  catch {
949
950
  // silent
950
951
  }
952
+ // PR billboard markdown (metadata only). Written before any early exit so
953
+ // --json runs and failed runs still get a comment.
954
+ if (options.commentFile) {
955
+ writeRunComment(options.commentFile, workflow, results, summarizeRun(results, wfDurationMs), {
956
+ trace,
957
+ durationsMs: Object.fromEntries(nodeTimer.durations),
958
+ });
959
+ }
951
960
  if (isJson) {
952
961
  process.stdout.write(JSON.stringify(Object.fromEntries(results), null, 2) + "\n");
953
962
  process.exit(wfHasFailed ? 1 : 0);
@@ -986,6 +995,12 @@ export async function workflowRunAction(file, options) {
986
995
  runLogger.flush();
987
996
  recordHistory(nodeTimer.lastResults, undefined, true);
988
997
  console.error(` ${renderReceiptLine(summarizeRun(new Map(), Date.now() - runStart, true), isTTY)}\n`);
998
+ // A crash must not leave a stale success comment behind.
999
+ if (options.commentFile) {
1000
+ writeRunComment(options.commentFile, workflow, new Map(), summarizeRun(new Map(), Date.now() - runStart, true), {
1001
+ crashed: true,
1002
+ });
1003
+ }
989
1004
  // Finalize the cloud run as failed (covers thrown errors, incl.
990
1005
  // RouteEvaluationError). Without this a crashed workflow run stays
991
1006
  // "running" in cloud forever.
@@ -1086,7 +1101,7 @@ workflowCmd
1086
1101
  console.warn("\x1B[33m ⚠ `sweny workflow create` is deprecated. Use `sweny new` instead.\x1B[0m\n");
1087
1102
  }
1088
1103
  const skills = configuredSkills();
1089
- const claude = new ClaudeClient({
1104
+ const claude = createHarness("claude-code", {
1090
1105
  maxTurns: 3,
1091
1106
  cwd: process.cwd(),
1092
1107
  logger: consoleLogger,
@@ -1153,7 +1168,7 @@ workflowCmd
1153
1168
  return;
1154
1169
  }
1155
1170
  const skills = configuredSkills();
1156
- const claude = new ClaudeClient({
1171
+ const claude = createHarness("claude-code", {
1157
1172
  maxTurns: 3,
1158
1173
  cwd: process.cwd(),
1159
1174
  logger: consoleLogger,
package/dist/cli/new.js CHANGED
@@ -14,7 +14,7 @@ import chalk from "chalk";
14
14
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
15
15
  import { WORKFLOW_TEMPLATES } from "./templates.js";
16
16
  import { buildWorkflow, refineWorkflow } from "../workflow-builder.js";
17
- import { ClaudeClient } from "../claude.js";
17
+ import { createHarness } from "../harness/index.js";
18
18
  import { consoleLogger } from "../types.js";
19
19
  import { configuredSkills } from "../skills/custom-loader.js";
20
20
  import { builtinSkills } from "../skills/index.js";
@@ -541,7 +541,7 @@ export async function runNew(options) {
541
541
  const { installMarketplaceWorkflow } = await import("./marketplace.js");
542
542
  const allSkills = configuredSkills(process.env, cwd);
543
543
  const hasAgent = !!process.env.ANTHROPIC_API_KEY;
544
- const claude = hasAgent ? new ClaudeClient({ maxTurns: 3, cwd, logger: consoleLogger }) : null;
544
+ const claude = hasAgent ? createHarness("claude-code", { maxTurns: 3, cwd, logger: consoleLogger }) : null;
545
545
  if (!options.skipIntro)
546
546
  p.intro(`Installing ${options.marketplaceId} from marketplace`);
547
547
  // Pre-compute source-control from git-remote (same method as the wizard path).
@@ -833,7 +833,7 @@ async function runCustomWorkflowBuilder(skills) {
833
833
  });
834
834
  if (p.isCancel(description))
835
835
  return null;
836
- const claude = new ClaudeClient({
836
+ const claude = createHarness("claude-code", {
837
837
  maxTurns: 3,
838
838
  cwd: process.cwd(),
839
839
  logger: consoleLogger,
@@ -48,6 +48,11 @@ export interface RunRecord {
48
48
  to: string;
49
49
  }>;
50
50
  totals: RunTotals;
51
+ /** Harness that ran the nodes (id + version). Absent for runs with no harness-tagged result. */
52
+ harness?: {
53
+ id: string;
54
+ version: string;
55
+ };
51
56
  }
52
57
  /** sha256 (hex) of the workflow as canonical JSON: keys sorted, undefined dropped. */
53
58
  export declare function hashWorkflow(workflow: Workflow): string;
@@ -91,6 +91,7 @@ export function buildRunRecord(i) {
91
91
  retries: retries.get(id) ?? 0,
92
92
  };
93
93
  });
94
+ const harness = [...i.results.values()].find((r) => r.harness)?.harness;
94
95
  return {
95
96
  schema_version: RUN_HISTORY_SCHEMA_VERSION,
96
97
  run_id: i.runId,
@@ -109,6 +110,7 @@ export function buildRunRecord(i) {
109
110
  tokens: summary.tokens ?? null,
110
111
  cost_usd: summary.costUsd ?? null,
111
112
  },
113
+ ...(harness ? { harness: { id: harness.id, version: harness.version } } : {}),
112
114
  };
113
115
  }
114
116
  // ── Write, prune, read ──────────────────────────────────────────
@@ -6,6 +6,7 @@
6
6
  * model prose: only counts, durations, token totals, and cost.
7
7
  */
8
8
  import { type ExecutionTrace, type Logger, type NodeResult, type Workflow } from "../types.js";
9
+ import { type NodeStatus } from "../mermaid.js";
9
10
  export interface RunSummary {
10
11
  ok: boolean;
11
12
  nodesOk: number;
@@ -31,6 +32,12 @@ export declare function formatCost(usd: number): string;
31
32
  export declare function formatReceipt(s: RunSummary): string;
32
33
  /** Receipt for a terminal: colored only when `color` is true. */
33
34
  export declare function renderReceiptLine(s: RunSummary, color: boolean): string;
35
+ /** Brand blue (blue-600 / blue-700), not indigo. Applied to successful nodes. */
36
+ export declare const STEP_SUMMARY_CLASS_DEFS: {
37
+ readonly success: "fill:#2563eb,stroke:#1d4ed8,color:#fff,stroke-width:2px";
38
+ };
39
+ /** Per-node Mermaid status from run results. */
40
+ export declare function nodeStates(results: Map<string, NodeResult>): Record<string, NodeStatus>;
34
41
  /** Markdown for `$GITHUB_STEP_SUMMARY`: receipt + status-colored Mermaid DAG. */
35
42
  export declare function formatStepSummary(workflow: Workflow, results: Map<string, NodeResult>, summary: RunSummary, trace?: ExecutionTrace): string;
36
43
  /**
@@ -94,15 +94,20 @@ export function renderReceiptLine(s, color) {
94
94
  }
95
95
  // ── Step summary ────────────────────────────────────────────────
96
96
  /** Brand blue (blue-600 / blue-700), not indigo. Applied to successful nodes. */
97
- const STEP_SUMMARY_CLASS_DEFS = {
97
+ export const STEP_SUMMARY_CLASS_DEFS = {
98
98
  success: "fill:#2563eb,stroke:#1d4ed8,color:#fff,stroke-width:2px",
99
99
  };
100
- /** Markdown for `$GITHUB_STEP_SUMMARY`: receipt + status-colored Mermaid DAG. */
101
- export function formatStepSummary(workflow, results, summary, trace) {
100
+ /** Per-node Mermaid status from run results. */
101
+ export function nodeStates(results) {
102
102
  const state = {};
103
103
  for (const [id, r] of results) {
104
104
  state[id] = r.status === "success" ? "success" : r.status === "failed" ? "failed" : "skipped";
105
105
  }
106
+ return state;
107
+ }
108
+ /** Markdown for `$GITHUB_STEP_SUMMARY`: receipt + status-colored Mermaid DAG. */
109
+ export function formatStepSummary(workflow, results, summary, trace) {
110
+ const state = nodeStates(results);
106
111
  return [
107
112
  `## ${summary.ok ? "✅" : "❌"} ${workflow.name}`,
108
113
  "",
@@ -185,5 +190,9 @@ export const WORKFLOW_RUN_OPTIONS = [
185
190
  "Show raw log lines and each tool call's input and output inline (human-readable, truncated). Use --stream for full untruncated NDJSON.",
186
191
  ],
187
192
  ["--mermaid", "Output a Mermaid diagram with execution state after run"],
193
+ [
194
+ "--comment-file <path>",
195
+ "Write a PR-comment markdown (run receipt, status-colored DAG, per-node table; metadata only) to <path> so any CI can post it",
196
+ ],
188
197
  ["--input <json>", "JSON string of input data to pass to the workflow"],
189
198
  ];
@@ -8,7 +8,10 @@ import { evaluateFunctionRule } from "./function.js";
8
8
  import { evaluateJudge } from "./judge.js";
9
9
  /** Reasoning cap, matching the spec's ~500-character ceiling. */
10
10
  const MAX_REASONING_LENGTH = 500;
11
- /** Default judge model when neither evaluator nor node nor workflow specify one. */
11
+ /**
12
+ * Judge model when neither evaluator, node, workflow nor the harness names one.
13
+ * Harnesses declare their own via `defaultJudgeModel` (Claude Code: this value).
14
+ */
12
15
  const DEFAULT_JUDGE_MODEL = "claude-haiku-4-5";
13
16
  function capReasoning(reasoning) {
14
17
  if (!reasoning)
@@ -17,8 +20,8 @@ function capReasoning(reasoning) {
17
20
  return reasoning;
18
21
  return reasoning.slice(0, MAX_REASONING_LENGTH - 1) + "…";
19
22
  }
20
- function resolveJudgeModel(evaluator, node, workflow) {
21
- return evaluator.model ?? node?.judge_model ?? workflow?.judge_model ?? DEFAULT_JUDGE_MODEL;
23
+ function resolveJudgeModel(evaluator, node, workflow, harnessDefault) {
24
+ return evaluator.model ?? node?.judge_model ?? workflow?.judge_model ?? harnessDefault ?? DEFAULT_JUDGE_MODEL;
22
25
  }
23
26
  /**
24
27
  * Run every declared evaluator against the node's result and return one
@@ -56,7 +59,7 @@ export async function evaluateAll(evaluators, result, opts = {}) {
56
59
  if (!opts.claude) {
57
60
  throw new Error(`evaluator '${e.name}' is kind: judge but no Claude client was provided to evaluateAll`);
58
61
  }
59
- const model = resolveJudgeModel(e, opts.node, opts.workflow);
62
+ const model = resolveJudgeModel(e, opts.node, opts.workflow, opts.claude.defaultJudgeModel);
60
63
  const verdict = await evaluateJudge(e, result, opts.claude, { model });
61
64
  out.push({
62
65
  name: e.name,
@@ -13,13 +13,22 @@
13
13
  * This replaces ~8k lines of engine + recipe step code.
14
14
  */
15
15
  import type { Workflow, Skill, Tool, Claude, Observer, Logger, ExecutionResult } from "./types.js";
16
+ import type { AgentHarness } from "./harness/types.js";
16
17
  export interface ExecuteOptions {
17
18
  /** Registered skills (id → Skill) */
18
19
  skills: Map<string, Skill>;
19
20
  /** Config values — env vars + explicit overrides */
20
21
  config?: Record<string, string>;
21
- /** Claude client */
22
- claude: Claude;
22
+ /**
23
+ * The agent harness that runs nodes (`createHarness("claude-code")`).
24
+ * Required unless the deprecated `claude` option is given.
25
+ */
26
+ harness?: AgentHarness;
27
+ /**
28
+ * Claude client.
29
+ * @deprecated Pass `harness`. Still accepted (see `claudeCompat()`); when both are set, `harness` wins.
30
+ */
31
+ claude?: Claude;
23
32
  /** Event observer for streaming/logging */
24
33
  observer?: Observer;
25
34
  /** Logger */
package/dist/executor.js CHANGED
@@ -22,6 +22,7 @@ import { buildToolAliases } from "./skills/index.js";
22
22
  import { validateWorkflow } from "./schema.js";
23
23
  import { resolveAgentAccess } from "./agent-env.js";
24
24
  import { fenceUntrusted } from "./untrusted.js";
25
+ import { asClaude } from "./harness/compat.js";
25
26
  /**
26
27
  * Default workflow-level step budget. Counts total node executions across the
27
28
  * whole run (loop iterations included). Generous enough for legitimate deep
@@ -47,7 +48,12 @@ function throwIfAborted(signal) {
47
48
  * - `trace`: full ordered execution trace including loops and routing decisions
48
49
  */
49
50
  export async function execute(workflow, input, options) {
50
- const { claude, observer, signal, timeoutMs } = options;
51
+ const { observer, signal, timeoutMs } = options;
52
+ // `harness` is the seam; a legacy `claude` object is used as-is (what `asClaude(claudeCompat(claude))` yields).
53
+ const claude = options.harness ? asClaude(options.harness) : options.claude;
54
+ if (!claude) {
55
+ throw new Error("execute() needs options.harness (or the deprecated options.claude)");
56
+ }
51
57
  // If the caller already aborted before we started, fail fast.
52
58
  throwIfAborted(signal);
53
59
  const dryRun = isDryRunInput(input);
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Declared capabilities per adapter. No runtime imports, so the executor can use
3
+ * them without pulling in an agent SDK.
4
+ */
5
+ import type { HarnessCapabilities } from "./types.js";
6
+ /** Claude Code enforces every sweny opinion natively, so `policyGate` never degrades or refuses it. */
7
+ export declare const CLAUDE_CODE_CAPABILITIES: HarnessCapabilities;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Declared capabilities per adapter. No runtime imports, so the executor can use
3
+ * them without pulling in an agent SDK.
4
+ */
5
+ /** Claude Code enforces every sweny opinion natively, so `policyGate` never degrades or refuses it. */
6
+ export const CLAUDE_CODE_CAPABILITIES = {
7
+ structuredOutput: "native",
8
+ toolTrace: "full",
9
+ builtinDeny: "by-name",
10
+ mcp: { inject: true, exclusive: "native" },
11
+ sandbox: { fs: true, network: true },
12
+ readOnly: "native",
13
+ turnLimit: "native",
14
+ usage: { tokens: true, costUsd: true, live: false },
15
+ cancel: "signal",
16
+ resume: false,
17
+ };