@sweny-ai/core 0.1.177 → 0.1.179
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.
- package/dist/agent-env.d.ts +5 -3
- package/dist/agent-env.js +12 -7
- package/dist/cli/config-file.d.ts +22 -1
- package/dist/cli/config-file.js +42 -0
- package/dist/cli/final-output.js +4 -1
- package/dist/cli/main.d.ts +2 -1
- package/dist/cli/main.js +7 -8
- package/dist/cli/resume.d.ts +10 -1
- package/dist/cli/resume.js +77 -7
- package/dist/cli/run-history.d.ts +3 -7
- package/dist/cli/run-history.js +1 -10
- package/dist/cli/run-output.d.ts +16 -10
- package/dist/cli/run-output.js +19 -13
- package/dist/cli/ticket.js +3 -2
- package/dist/decider.d.ts +176 -64
- package/dist/decider.js +475 -155
- package/dist/executor.d.ts +50 -7
- package/dist/executor.js +533 -93
- package/dist/harness/acp.js +2 -0
- package/dist/harness/claude-code.d.ts +18 -0
- package/dist/harness/claude-code.js +51 -6
- package/dist/harness/codex.js +2 -0
- package/dist/harness/pi.js +2 -0
- package/dist/harness/sandbox-wrapper.js +8 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/journal.d.ts +258 -74
- package/dist/journal.js +749 -208
- package/dist/schema.d.ts +189 -241
- package/dist/schema.js +56 -36
- package/dist/skills/github.js +39 -0
- package/dist/types.d.ts +55 -3
- package/dist/types.js +22 -0
- package/dist/when.d.ts +8 -1
- package/dist/when.js +8 -1
- package/dist/workflow-builder.js +3 -1
- package/dist/workflows/browser.js +2 -2
- package/dist/workflows/implement.yml +37 -5
- package/dist/workflows/triage.yml +40 -6
- package/package.json +1 -1
package/dist/harness/acp.js
CHANGED
|
@@ -713,6 +713,8 @@ export class AcpHarness {
|
|
|
713
713
|
const extraDegraded = [];
|
|
714
714
|
const tag = (r) => ({
|
|
715
715
|
...r,
|
|
716
|
+
// Only the process wrapper keeps the agent out of the sweny state dir.
|
|
717
|
+
contained: wrapper !== undefined,
|
|
716
718
|
harness: this.info(),
|
|
717
719
|
degraded: [...degraded, ...extraDegraded],
|
|
718
720
|
});
|
|
@@ -51,6 +51,24 @@ export declare const CLAUDE_TOOLS_BY_CLASS: Readonly<Record<ToolClass, readonly
|
|
|
51
51
|
* `denyRead` (merged with these rules) covers Bash.
|
|
52
52
|
*/
|
|
53
53
|
export declare function claudeReadDenyRule(absPath: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Deny rules keeping every built-in file tool out of the run journals' state
|
|
56
|
+
* dir ({@link runStateRoot}): an agent that could read a run key could forge
|
|
57
|
+
* journal records, and one that could write there could cut a journal or
|
|
58
|
+
* plant a run.
|
|
59
|
+
*
|
|
60
|
+
* Mechanism: scoped deny rules on `disallowedTools`. Per the Agent SDK's
|
|
61
|
+
* permission evaluation order (code.claude.com/docs/en/agent-sdk/permissions,
|
|
62
|
+
* "Deny rules"), a matching deny rule from `disallowed_tools` blocks the tool
|
|
63
|
+
* "even in `bypassPermissions` mode", which every node runs under (#365);
|
|
64
|
+
* `canUseTool` and allow rules are never reached in that mode. `Read(path)`
|
|
65
|
+
* rules govern the built-in file readers (Read, and Grep/Glob), and
|
|
66
|
+
* `Edit(path)` rules "govern all built-in tools that write files, including
|
|
67
|
+
* `Write` and `NotebookEdit`" (a `Write(path)` rule is never matched, so none
|
|
68
|
+
* is emitted). `//` anchors an absolute filesystem path. Both the path as
|
|
69
|
+
* configured and its real path are denied (macOS `/var` is `/private/var`).
|
|
70
|
+
*/
|
|
71
|
+
export declare function claudeStateDirDenyRules(root?: string): string[];
|
|
54
72
|
/** Native `disallowedTools` for a policy: legacy names, compiled classes, and the read-only set. */
|
|
55
73
|
export declare function compileClaudeCodeDeny(policy: NodePolicy, legacy?: readonly string[]): string[];
|
|
56
74
|
/** How sweny resolves which credentials reach the Claude Code subprocess. */
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { query, createSdkMcpServer, tool as sdkTool } from "@anthropic-ai/claude-agent-sdk";
|
|
12
12
|
import { createRequire } from "node:module";
|
|
13
|
+
import { realpathSync } from "node:fs";
|
|
13
14
|
import { consoleLogger } from "../types.js";
|
|
14
15
|
import { AGENT_AUTH_VARS, finishAgentEnv, reportWithheldEnv, parseList, resolveEnvScope, scopeAgentEnv, withPushBlocked, mcpWithheld, resolveAgentSandbox, } from "../agent-env.js";
|
|
15
16
|
import { gitCredentialGap, policyGate } from "./policy.js";
|
|
@@ -20,6 +21,7 @@ import { jsonSchemaToZodShape, toolErrorToMcpResult, toolOutputToMcpResult } fro
|
|
|
20
21
|
import { startToolBridge } from "./tool-bridge/server.js";
|
|
21
22
|
import { parseToolResultContent, summarizeToolError, tryParseJSON } from "./parse.js";
|
|
22
23
|
import { makeAbort } from "./abort.js";
|
|
24
|
+
import { runStateRoot } from "../journal.js";
|
|
23
25
|
import { claudeCodeAuth } from "./auth.js";
|
|
24
26
|
export { buildEvaluatePrompt, CLAUDE_CODE_CAPABILITIES };
|
|
25
27
|
export { parseToolResultContent, summarizeToolError, makeAbort };
|
|
@@ -96,6 +98,39 @@ export const CLAUDE_TOOLS_BY_CLASS = {
|
|
|
96
98
|
export function claudeReadDenyRule(absPath) {
|
|
97
99
|
return `Read(/${absPath.startsWith("/") ? absPath : `/${absPath}`})`;
|
|
98
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Deny rules keeping every built-in file tool out of the run journals' state
|
|
103
|
+
* dir ({@link runStateRoot}): an agent that could read a run key could forge
|
|
104
|
+
* journal records, and one that could write there could cut a journal or
|
|
105
|
+
* plant a run.
|
|
106
|
+
*
|
|
107
|
+
* Mechanism: scoped deny rules on `disallowedTools`. Per the Agent SDK's
|
|
108
|
+
* permission evaluation order (code.claude.com/docs/en/agent-sdk/permissions,
|
|
109
|
+
* "Deny rules"), a matching deny rule from `disallowed_tools` blocks the tool
|
|
110
|
+
* "even in `bypassPermissions` mode", which every node runs under (#365);
|
|
111
|
+
* `canUseTool` and allow rules are never reached in that mode. `Read(path)`
|
|
112
|
+
* rules govern the built-in file readers (Read, and Grep/Glob), and
|
|
113
|
+
* `Edit(path)` rules "govern all built-in tools that write files, including
|
|
114
|
+
* `Write` and `NotebookEdit`" (a `Write(path)` rule is never matched, so none
|
|
115
|
+
* is emitted). `//` anchors an absolute filesystem path. Both the path as
|
|
116
|
+
* configured and its real path are denied (macOS `/var` is `/private/var`).
|
|
117
|
+
*/
|
|
118
|
+
export function claudeStateDirDenyRules(root = runStateRoot()) {
|
|
119
|
+
let real = root;
|
|
120
|
+
try {
|
|
121
|
+
real = realpathSync(root);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// not created yet: the configured path is the one tools would use
|
|
125
|
+
}
|
|
126
|
+
const abs = (p) => (p.startsWith("/") ? p : `/${p}`);
|
|
127
|
+
return [...new Set([root, real])].flatMap((dir) => [
|
|
128
|
+
`Read(/${abs(dir)})`,
|
|
129
|
+
`Read(/${abs(dir)}/**)`,
|
|
130
|
+
`Edit(/${abs(dir)})`,
|
|
131
|
+
`Edit(/${abs(dir)}/**)`,
|
|
132
|
+
]);
|
|
133
|
+
}
|
|
99
134
|
/** Native `disallowedTools` for a policy: legacy names, compiled classes, and the read-only set. */
|
|
100
135
|
export function compileClaudeCodeDeny(policy, legacy = []) {
|
|
101
136
|
return [
|
|
@@ -344,6 +379,8 @@ export class ClaudeCodeHarness {
|
|
|
344
379
|
harness: this.info(),
|
|
345
380
|
degraded,
|
|
346
381
|
...(this.lastPolicyFacts ? { policy: this.lastPolicyFacts } : {}),
|
|
382
|
+
// The state dir deny rules hold for the file tools either way; Bash is held only by the sandbox.
|
|
383
|
+
contained: this.lastPolicyFacts?.sandboxStarted === true,
|
|
347
384
|
};
|
|
348
385
|
}
|
|
349
386
|
async runQuery(opts) {
|
|
@@ -354,6 +391,9 @@ export class ClaudeCodeHarness {
|
|
|
354
391
|
let disallowedTools = readOnly
|
|
355
392
|
? [...new Set([...(opts.disallowedTools ?? []), ...READ_ONLY_DISALLOWED_TOOLS])]
|
|
356
393
|
: opts.disallowedTools;
|
|
394
|
+
// Run journals, their keys and locks live in the sweny state dir: no built-in
|
|
395
|
+
// file tool may read or write there (Bash is held by the sandbox's filesystem deny).
|
|
396
|
+
disallowedTools = [...new Set([...(disallowedTools ?? []), ...claudeStateDirDenyRules()])];
|
|
357
397
|
const effectiveModel = model ?? this.model;
|
|
358
398
|
// #360: run agent commands in the SDK sandbox. `auto` (default in CI) falls
|
|
359
399
|
// back to unsandboxed with one loud warning when the host cannot sandbox;
|
|
@@ -517,12 +557,17 @@ export class ClaudeCodeHarness {
|
|
|
517
557
|
...(abort ? { abortController: abort.controller } : {}),
|
|
518
558
|
...(effectiveModel ? { model: effectiveModel } : {}),
|
|
519
559
|
...(Object.keys(allMcpServers).length > 0 ? { mcpServers: allMcpServers } : {}),
|
|
520
|
-
//
|
|
521
|
-
//
|
|
522
|
-
//
|
|
523
|
-
//
|
|
524
|
-
//
|
|
525
|
-
|
|
560
|
+
// Only what sweny passes runs: an ambient MCP server or plugin (user
|
|
561
|
+
// settings, a repo's .mcp.json or .claude/settings.json, which are
|
|
562
|
+
// repo content) could read the run journals' state dir around the
|
|
563
|
+
// sandbox and the deny rules. SDK Options (sdk.d.ts):
|
|
564
|
+
// `strictMcpConfig` "Only use MCP servers passed via the `mcpServers`
|
|
565
|
+
// option ... ignoring ... project `.mcp.json`, user settings,
|
|
566
|
+
// plugins"; `settingSources` "Pass `[]` to disable filesystem
|
|
567
|
+
// settings (SDK isolation mode)", so no user/project/local
|
|
568
|
+
// settings, hooks or settings-enabled plugins load either.
|
|
569
|
+
strictMcpConfig: true,
|
|
570
|
+
settingSources: [],
|
|
526
571
|
...(disallowedTools && disallowedTools.length > 0 ? { disallowedTools } : {}),
|
|
527
572
|
// CC-08: ask the SDK to produce validated structured output when the
|
|
528
573
|
// node declares an output schema. The SDK then returns the parsed
|
package/dist/harness/codex.js
CHANGED
|
@@ -758,6 +758,8 @@ export class CodexHarness {
|
|
|
758
758
|
let degraded = gate.degraded;
|
|
759
759
|
const tag = (r, extra = []) => ({
|
|
760
760
|
...r,
|
|
761
|
+
// Only the process wrapper keeps Codex out of the sweny state dir (its own sandbox allows reads).
|
|
762
|
+
contained: wrapper !== undefined,
|
|
761
763
|
harness: this.info(),
|
|
762
764
|
degraded: [...degraded, ...extra],
|
|
763
765
|
});
|
package/dist/harness/pi.js
CHANGED
|
@@ -756,6 +756,8 @@ export class PiHarness {
|
|
|
756
756
|
const extraDegraded = [];
|
|
757
757
|
const tag = (r) => ({
|
|
758
758
|
...r,
|
|
759
|
+
// Only the process wrapper keeps the agent out of the sweny state dir.
|
|
760
|
+
contained: wrapper !== undefined,
|
|
759
761
|
harness: this.info(),
|
|
760
762
|
degraded: [...degraded, ...extraDegraded],
|
|
761
763
|
});
|
|
@@ -48,7 +48,7 @@ import { lstat, mkdir, mkdtemp, realpath, rm, writeFile } from "node:fs/promises
|
|
|
48
48
|
import { homedir, tmpdir } from "node:os";
|
|
49
49
|
import * as path from "node:path";
|
|
50
50
|
import { DEFAULT_SANDBOX_DOMAINS, parseList, resolveSandboxMode } from "../agent-env.js";
|
|
51
|
-
import {
|
|
51
|
+
import { runStateRoot } from "../journal.js";
|
|
52
52
|
import { policyGate } from "./policy.js";
|
|
53
53
|
// ─── srt settings ────────────────────────────────────────────────
|
|
54
54
|
/**
|
|
@@ -132,13 +132,13 @@ export function buildSrtSettings(req, opts) {
|
|
|
132
132
|
// cannot rename one out from under its read deny.
|
|
133
133
|
const masked = [...new Set((req.denyRead ?? []).map(real))].filter((p) => exists(p));
|
|
134
134
|
denyRead.push(...masked.filter((p) => !denyRead.includes(p)));
|
|
135
|
-
// Run
|
|
136
|
-
//
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const denyWrite =
|
|
135
|
+
// Run journals, their keys and locks (the sweny state dir, outside the
|
|
136
|
+
// workspace) are neither readable nor writable by the agent.
|
|
137
|
+
const journals = real(runStateRoot());
|
|
138
|
+
const journalsExist = exists(journals);
|
|
139
|
+
if (journalsExist && !denyRead.includes(journals))
|
|
140
|
+
denyRead.push(journals);
|
|
141
|
+
const denyWrite = journalsExist ? [...masked, journals] : masked;
|
|
142
142
|
const allowWrite = req.readOnly ? [opts.home] : [real(req.cwd), opts.home];
|
|
143
143
|
return {
|
|
144
144
|
network: { allowedDomains, deniedDomains: [], strictAllowlist: true, allowLocalBinding: false },
|
package/dist/index.d.ts
CHANGED
|
@@ -27,8 +27,8 @@ export { EVALUATOR_KINDS, EVAL_POLICIES, REQUIRES_ON_FAIL, NODE_ON_FAIL, MCP_TRA
|
|
|
27
27
|
export { consoleLogger } from "./types.js";
|
|
28
28
|
export { execute, RouteEvaluationError } from "./executor.js";
|
|
29
29
|
export type { ExecuteOptions } from "./executor.js";
|
|
30
|
-
export { systemOneProvider
|
|
31
|
-
export type {
|
|
30
|
+
export { systemOneProvider } from "./decider.js";
|
|
31
|
+
export type { DeciderConfig, DeciderOperatorConfig } from "./decider.js";
|
|
32
32
|
export { BudgetGuard, describeOverrun, minLimits, toLimits } from "./budget.js";
|
|
33
33
|
export type { Budget, BudgetOverrun, BudgetUnit, SpendLimits } from "./budget.js";
|
|
34
34
|
export { resolveNodePermissions, buildNodePolicy } from "./node-policy.js";
|
package/dist/index.js
CHANGED
|
@@ -27,8 +27,8 @@ export { EVALUATOR_KINDS, EVAL_POLICIES, REQUIRES_ON_FAIL, NODE_ON_FAIL, MCP_TRA
|
|
|
27
27
|
export { consoleLogger } from "./types.js";
|
|
28
28
|
// Executor
|
|
29
29
|
export { execute, RouteEvaluationError } from "./executor.js";
|
|
30
|
-
// Decision models
|
|
31
|
-
export { systemOneProvider
|
|
30
|
+
// Decision models (#357): config types and the provider adapter only
|
|
31
|
+
export { systemOneProvider } from "./decider.js";
|
|
32
32
|
// Least-privilege nodes and safe outputs (#365)
|
|
33
33
|
export { BudgetGuard, describeOverrun, minLimits, toLimits } from "./budget.js";
|
|
34
34
|
export { resolveNodePermissions, buildNodePolicy } from "./node-policy.js";
|