@sweny-ai/core 0.1.148 → 0.1.149

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.
@@ -6,6 +6,10 @@
6
6
  */
7
7
  export declare const SUPPORTED_AGENTS: readonly ["claude", "codex", "pi"];
8
8
  export type SupportedAgent = (typeof SUPPORTED_AGENTS)[number];
9
- export declare function isSupportedAgent(id: string): id is SupportedAgent;
9
+ /** `acp:<command>` (#416): any Agent Client Protocol agent, started with `<command>`. */
10
+ export type AcpAgentId = `acp:${string}`;
11
+ /** The command of an `acp:<command>` agent id (`"opencode acp"` for `acp:opencode acp`), or undefined when it is not one or the command is empty. */
12
+ export declare function parseAcpAgent(id: string): string | undefined;
13
+ export declare function isSupportedAgent(id: string): id is SupportedAgent | AcpAgentId;
10
14
  /** The error for an agent id with no adapter. */
11
15
  export declare function unsupportedAgentError(id: string): string;
@@ -5,8 +5,15 @@
5
5
  * the harness factory share it).
6
6
  */
7
7
  export const SUPPORTED_AGENTS = ["claude", "codex", "pi"];
8
+ /** The command of an `acp:<command>` agent id (`"opencode acp"` for `acp:opencode acp`), or undefined when it is not one or the command is empty. */
9
+ export function parseAcpAgent(id) {
10
+ if (!id.startsWith("acp:"))
11
+ return undefined;
12
+ const command = id.slice("acp:".length).trim();
13
+ return command === "" ? undefined : command;
14
+ }
8
15
  export function isSupportedAgent(id) {
9
- return SUPPORTED_AGENTS.includes(id);
16
+ return SUPPORTED_AGENTS.includes(id) || parseAcpAgent(id) !== undefined;
10
17
  }
11
18
  /** The error for an agent id with no adapter. */
12
19
  export function unsupportedAgentError(id) {
@@ -56,3 +56,33 @@ export declare const CODEX_CAPABILITIES: HarnessCapabilities;
56
56
  * - cancel rpc: the `abort` command, then SIGTERM and SIGKILL.
57
57
  */
58
58
  export declare const PI_CAPABILITIES: HarnessCapabilities;
59
+ /**
60
+ * Any ACP agent (`--agent acp:<command>`, #416), declared from the Agent
61
+ * Client Protocol schema v1.24.1 (agentclientprotocol/agent-client-protocol,
62
+ * `schema/v1/schema.json`, docs under `docs/protocol/v1/`) and probed by the
63
+ * contract suite against a scripted fake agent. ACP is the long-tail adapter:
64
+ * the protocol carries a prompt and a stream of updates, not sweny's opinions.
65
+ * - structuredOutput prompt: `PromptResponse` has only `stopReason`, so there
66
+ * is no schema channel. sweny asks for the JSON in the prompt, parses it,
67
+ * checks it and retries once.
68
+ * - toolTrace full: `tool_call` and `tool_call_update` session updates carry an
69
+ * id, a kind, a status and (optionally) raw input and content.
70
+ * - builtinDeny none: the protocol has no tool allow or deny list. The only
71
+ * hook is `session/request_permission`, which fires only when the agent
72
+ * chooses to ask. sweny answers it by policy (best effort), but cannot
73
+ * promise the agent asks, so nothing is declared.
74
+ * - mcp inject, exclusive none: `session/new` takes `mcpServers`, but whether
75
+ * the agent also loads its own MCP config is agent-defined.
76
+ * - sandbox none: nothing in the protocol. The process wrapper (srt) is the
77
+ * only containment, see sandbox-wrapper.ts.
78
+ * - readOnly none: modes are agent-defined. `policyGate` counts read-only as
79
+ * enforced only with the wrapper's read-only mount.
80
+ * - turnLimit watchdog: no turn limit in the protocol; sweny counts tool calls.
81
+ * - usage costUsd only: `usage_update {used, size, cost?}` is the stable
82
+ * usage. `used` and `size` are context window occupancy, not billed tokens,
83
+ * so they are not mapped; `cost` (cumulative, ISO 4217 currency) is, when it
84
+ * is USD. Per-turn token usage is behind an unstable flag.
85
+ * - cancel rpc: `session/cancel`, then kill.
86
+ * - resume false: sweny starts a fresh session per node.
87
+ */
88
+ export declare const ACP_CAPABILITIES: HarnessCapabilities;
@@ -90,3 +90,44 @@ export const PI_CAPABILITIES = {
90
90
  cancel: "rpc",
91
91
  resume: false,
92
92
  };
93
+ /**
94
+ * Any ACP agent (`--agent acp:<command>`, #416), declared from the Agent
95
+ * Client Protocol schema v1.24.1 (agentclientprotocol/agent-client-protocol,
96
+ * `schema/v1/schema.json`, docs under `docs/protocol/v1/`) and probed by the
97
+ * contract suite against a scripted fake agent. ACP is the long-tail adapter:
98
+ * the protocol carries a prompt and a stream of updates, not sweny's opinions.
99
+ * - structuredOutput prompt: `PromptResponse` has only `stopReason`, so there
100
+ * is no schema channel. sweny asks for the JSON in the prompt, parses it,
101
+ * checks it and retries once.
102
+ * - toolTrace full: `tool_call` and `tool_call_update` session updates carry an
103
+ * id, a kind, a status and (optionally) raw input and content.
104
+ * - builtinDeny none: the protocol has no tool allow or deny list. The only
105
+ * hook is `session/request_permission`, which fires only when the agent
106
+ * chooses to ask. sweny answers it by policy (best effort), but cannot
107
+ * promise the agent asks, so nothing is declared.
108
+ * - mcp inject, exclusive none: `session/new` takes `mcpServers`, but whether
109
+ * the agent also loads its own MCP config is agent-defined.
110
+ * - sandbox none: nothing in the protocol. The process wrapper (srt) is the
111
+ * only containment, see sandbox-wrapper.ts.
112
+ * - readOnly none: modes are agent-defined. `policyGate` counts read-only as
113
+ * enforced only with the wrapper's read-only mount.
114
+ * - turnLimit watchdog: no turn limit in the protocol; sweny counts tool calls.
115
+ * - usage costUsd only: `usage_update {used, size, cost?}` is the stable
116
+ * usage. `used` and `size` are context window occupancy, not billed tokens,
117
+ * so they are not mapped; `cost` (cumulative, ISO 4217 currency) is, when it
118
+ * is USD. Per-turn token usage is behind an unstable flag.
119
+ * - cancel rpc: `session/cancel`, then kill.
120
+ * - resume false: sweny starts a fresh session per node.
121
+ */
122
+ export const ACP_CAPABILITIES = {
123
+ structuredOutput: "prompt",
124
+ toolTrace: "full",
125
+ builtinDeny: "none",
126
+ mcp: { inject: true, exclusive: "none" },
127
+ sandbox: { fs: false, network: false },
128
+ readOnly: "none",
129
+ turnLimit: "watchdog",
130
+ usage: { tokens: false, costUsd: true, live: false },
131
+ cancel: "rpc",
132
+ resume: false,
133
+ };
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { ClaudeCodeHarness, type ClaudeCodeHarnessOptions } from "./claude-code.js";
6
6
  import { CodexHarness, type CodexHarnessOptions } from "./codex.js";
7
+ import { AcpHarness, type AcpHarnessOptions } from "./acp.js";
7
8
  import { PiHarness, type PiHarnessOptions } from "./pi.js";
8
9
  export { claudeCompat, asClaude } from "./compat.js";
9
10
  export type { AgentHarness, HarnessCapabilities, HarnessCompleteRequest, HarnessId, HarnessInfo, HarnessRunRequest, HarnessRunResult, NodePolicy, PolicyGateResult, PolicyWrappers, ToolClass, } from "./types.js";
@@ -14,12 +15,14 @@ export { ClaudeCodeHarness, CLAUDE_CODE_CAPABILITIES } from "./claude-code.js";
14
15
  export type { ClaudeCodeHarnessOptions } from "./claude-code.js";
15
16
  export { CodexHarness, CODEX_CAPABILITIES, MIN_CODEX_VERSION } from "./codex.js";
16
17
  export type { CodexHarnessOptions } from "./codex.js";
18
+ export { AcpHarness, ACP_CAPABILITIES, ACP_PROTOCOL_VERSION } from "./acp.js";
19
+ export type { AcpHarnessOptions } from "./acp.js";
17
20
  export { PiHarness, PI_CAPABILITIES, MIN_PI_VERSION } from "./pi.js";
18
21
  export type { PiHarnessOptions } from "./pi.js";
19
22
  export { startToolBridge } from "./tool-bridge/server.js";
20
23
  export type { ToolBridge, ToolBridgeOptions } from "./tool-bridge/server.js";
21
- export { SUPPORTED_AGENTS, isSupportedAgent, unsupportedAgentError } from "./agents.js";
22
- export type { SupportedAgent } from "./agents.js";
24
+ export { SUPPORTED_AGENTS, isSupportedAgent, parseAcpAgent, unsupportedAgentError } from "./agents.js";
25
+ export type { AcpAgentId, SupportedAgent } from "./agents.js";
23
26
  /**
24
27
  * Build the harness for an agent id. `claude` is the historical `--agent` /
25
28
  * `coding-agent-provider` value and means the same as `claude-code`.
@@ -28,4 +31,5 @@ export type { SupportedAgent } from "./agents.js";
28
31
  export declare function createHarness(id: "claude" | "claude-code", opts?: ClaudeCodeHarnessOptions): ClaudeCodeHarness;
29
32
  export declare function createHarness(id: "codex", opts?: CodexHarnessOptions): CodexHarness;
30
33
  export declare function createHarness(id: "pi", opts?: PiHarnessOptions): PiHarness;
31
- export declare function createHarness(id: string, opts?: ClaudeCodeHarnessOptions & CodexHarnessOptions & PiHarnessOptions): ClaudeCodeHarness | CodexHarness | PiHarness;
34
+ export declare function createHarness(id: `acp:${string}`, opts?: AcpHarnessOptions): AcpHarness;
35
+ export declare function createHarness(id: string, opts?: ClaudeCodeHarnessOptions & CodexHarnessOptions & PiHarnessOptions & AcpHarnessOptions): ClaudeCodeHarness | CodexHarness | PiHarness | AcpHarness;
@@ -4,17 +4,22 @@
4
4
  */
5
5
  import { ClaudeCodeHarness } from "./claude-code.js";
6
6
  import { CodexHarness } from "./codex.js";
7
+ import { AcpHarness } from "./acp.js";
7
8
  import { PiHarness } from "./pi.js";
8
- import { unsupportedAgentError } from "./agents.js";
9
+ import { parseAcpAgent, unsupportedAgentError } from "./agents.js";
9
10
  export { claudeCompat, asClaude } from "./compat.js";
10
11
  export { policyGate, nativeDenyClasses, resolveHarnessPolicy, isToolClass, TOOL_CLASSES } from "./policy.js";
11
12
  export { ask, evaluate, buildAskPrompt, buildEvaluatePrompt } from "./prompts.js";
12
13
  export { ClaudeCodeHarness, CLAUDE_CODE_CAPABILITIES } from "./claude-code.js";
13
14
  export { CodexHarness, CODEX_CAPABILITIES, MIN_CODEX_VERSION } from "./codex.js";
15
+ export { AcpHarness, ACP_CAPABILITIES, ACP_PROTOCOL_VERSION } from "./acp.js";
14
16
  export { PiHarness, PI_CAPABILITIES, MIN_PI_VERSION } from "./pi.js";
15
17
  export { startToolBridge } from "./tool-bridge/server.js";
16
- export { SUPPORTED_AGENTS, isSupportedAgent, unsupportedAgentError } from "./agents.js";
18
+ export { SUPPORTED_AGENTS, isSupportedAgent, parseAcpAgent, unsupportedAgentError } from "./agents.js";
17
19
  export function createHarness(id, opts = {}) {
20
+ // `acp:<command>` (#416): any ACP agent. An empty command is an error, not a fallback.
21
+ if (id.startsWith("acp:"))
22
+ return new AcpHarness({ ...opts, acpCommand: parseAcpAgent(id) ?? "" });
18
23
  switch (id) {
19
24
  case "claude":
20
25
  case "claude-code":
@@ -47,3 +47,15 @@ export declare function summarizeToolError(parsed: unknown): string;
47
47
  * non-conforming model answer is at least attributable.
48
48
  */
49
49
  export declare function tryParseJSON(text: string, outputSchema?: JSONSchema, logger?: Pick<Logger, "warn">, label?: string): Record<string, unknown>;
50
+ /**
51
+ * Lightweight structural check of a parsed object against a JSON Schema.
52
+ *
53
+ * Deliberately shallow: it verifies `required` keys are present and that the
54
+ * top-level `type` and each declared property `type` match. It does NOT do
55
+ * full JSON Schema validation (no `$ref`, `oneOf`, formats, nested arrays).
56
+ * The contract here is "flag an obviously non-conforming object" so a mismatch
57
+ * is logged rather than silently accepted; it is not a validation gate.
58
+ *
59
+ * Returns a list of human-readable problems; empty means no detected mismatch.
60
+ */
61
+ export declare function schemaMismatches(value: unknown, schema: JSONSchema): string[];
@@ -195,7 +195,7 @@ export function tryParseJSON(text, outputSchema, logger, label = "Claude") {
195
195
  *
196
196
  * Returns a list of human-readable problems; empty means no detected mismatch.
197
197
  */
198
- function schemaMismatches(value, schema) {
198
+ export function schemaMismatches(value, schema) {
199
199
  const problems = [];
200
200
  const s = schema;
201
201
  if (s.type === "object" || s.properties || s.required) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sweny-ai/core",
3
- "version": "0.1.148",
3
+ "version": "0.1.149",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sweny": "./dist/cli/main.js"