@sweny-ai/core 0.1.142 → 0.1.144
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 +24 -0
- package/dist/agent-env.js +40 -7
- package/dist/cli/config-file.d.ts +1 -1
- package/dist/cli/config-file.js +1 -1
- package/dist/cli/config.d.ts +2 -0
- package/dist/cli/config.js +15 -7
- package/dist/cli/main.js +30 -4
- package/dist/cli/run-history.d.ts +2 -0
- package/dist/cli/run-history.js +2 -0
- package/dist/cli/run-output.d.ts +13 -2
- package/dist/cli/run-output.js +27 -2
- package/dist/executor.d.ts +12 -0
- package/dist/executor.js +127 -7
- package/dist/harness/abort.d.ts +17 -0
- package/dist/harness/abort.js +53 -0
- package/dist/harness/agents.d.ts +11 -0
- package/dist/harness/agents.js +15 -0
- package/dist/harness/capabilities.d.ts +23 -0
- package/dist/harness/capabilities.js +35 -0
- package/dist/harness/claude-code.d.ts +20 -43
- package/dist/harness/claude-code.js +58 -312
- package/dist/harness/codex.d.ts +175 -0
- package/dist/harness/codex.js +921 -0
- package/dist/harness/index.d.ts +11 -3
- package/dist/harness/index.js +8 -8
- package/dist/harness/parse.d.ts +49 -0
- package/dist/harness/parse.js +253 -0
- package/dist/harness/policy.d.ts +23 -1
- package/dist/harness/policy.js +73 -15
- package/dist/harness/prompts.d.ts +7 -1
- package/dist/harness/prompts.js +16 -0
- package/dist/harness/types.d.ts +13 -2
- package/dist/harness/types.js +2 -2
- package/dist/index.d.ts +8 -4
- package/dist/index.js +6 -3
- package/dist/node-policy.d.ts +31 -0
- package/dist/node-policy.js +42 -0
- package/dist/safe-outputs.d.ts +103 -0
- package/dist/safe-outputs.js +460 -0
- package/dist/schema.d.ts +501 -12
- package/dist/schema.js +210 -4
- package/dist/skills/github.js +18 -0
- package/dist/types.d.ts +107 -1
- package/dist/types.js +29 -0
- package/package.json +1 -1
package/dist/schema.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* parsing untrusted input (JSON/YAML import, Studio editor).
|
|
8
8
|
*/
|
|
9
9
|
import { z } from "zod";
|
|
10
|
-
import { EVALUATOR_KINDS, EVAL_POLICIES, MCP_TRANSPORTS, NODE_ON_FAIL, REQUIRES_ON_FAIL, SKILL_CATEGORIES, SKILL_ID_MAX_LENGTH, SKILL_ID_PATTERN, TOOL_ACCESS, WORKFLOW_TYPES, } from "./types.js";
|
|
10
|
+
import { AUTHOR_ASSOCIATIONS, EVALUATOR_KINDS, EVAL_POLICIES, MCP_TRANSPORTS, NODE_ACCESS, NODE_ON_FAIL, REQUIRES_ON_FAIL, SAFE_OUTPUT_APPLIERS, SAFE_OUTPUT_EXPIRES_PATTERN, SAFE_OUTPUT_MAX_CEILING, SAFE_OUTPUT_TYPES, SKILL_CATEGORIES, SKILL_ID_MAX_LENGTH, SKILL_ID_PATTERN, TOOL_ACCESS, TOOL_CLASSES, WORKFLOW_TYPES, } from "./types.js";
|
|
11
11
|
import { sourceZ } from "./sources.js";
|
|
12
12
|
import { workflowInputsZ, WORKFLOW_INPUT_TYPES } from "./inputs.js";
|
|
13
13
|
export { sourceZ };
|
|
@@ -244,6 +244,47 @@ export const nodeRetryZ = z
|
|
|
244
244
|
instruction: z.union([z.string().min(1), retryInstructionAutoZ, retryInstructionReflectZ]).optional(),
|
|
245
245
|
})
|
|
246
246
|
.strict();
|
|
247
|
+
/**
|
|
248
|
+
* Node / workflow permissions (#365): `read` | `write`, or an object with
|
|
249
|
+
* `access`, `deny` (portable tool classes) and `strict`. The object form must
|
|
250
|
+
* declare at least one key.
|
|
251
|
+
*/
|
|
252
|
+
export const nodePermissionsZ = z.union([
|
|
253
|
+
z.enum(NODE_ACCESS),
|
|
254
|
+
z
|
|
255
|
+
.object({
|
|
256
|
+
access: z.enum(NODE_ACCESS).optional(),
|
|
257
|
+
deny: z.array(z.enum(TOOL_CLASSES)).min(1).optional(),
|
|
258
|
+
strict: z.boolean().optional(),
|
|
259
|
+
})
|
|
260
|
+
.strict()
|
|
261
|
+
.refine((p) => p.access !== undefined || p.deny !== undefined || p.strict !== undefined, {
|
|
262
|
+
message: "permissions must declare at least one of access, deny, strict",
|
|
263
|
+
}),
|
|
264
|
+
]);
|
|
265
|
+
/** One typed write intent a node may emit (#365). */
|
|
266
|
+
export const safeOutputDeclarationZ = z
|
|
267
|
+
.object({
|
|
268
|
+
type: z.enum(SAFE_OUTPUT_TYPES),
|
|
269
|
+
via: z.string().min(1).optional(),
|
|
270
|
+
max: z.number().int().min(1).max(SAFE_OUTPUT_MAX_CEILING).optional(),
|
|
271
|
+
target: z.string().min(1).optional(),
|
|
272
|
+
title_prefix: z.string().min(1).max(64).optional(),
|
|
273
|
+
labels: z.array(z.string().min(1)).min(1).optional(),
|
|
274
|
+
expires: z.string().regex(SAFE_OUTPUT_EXPIRES_PATTERN).optional(),
|
|
275
|
+
})
|
|
276
|
+
.strict();
|
|
277
|
+
/** Workflow-level safe-output policy (#365): the ceiling and run-wide limits. */
|
|
278
|
+
export const safeOutputsPolicyZ = z
|
|
279
|
+
.object({
|
|
280
|
+
allow: z.array(z.enum(SAFE_OUTPUT_TYPES)).min(1).optional(),
|
|
281
|
+
max: z.number().int().min(1).max(SAFE_OUTPUT_MAX_CEILING).optional(),
|
|
282
|
+
staged: z.boolean().optional(),
|
|
283
|
+
trusted_actors: z.array(z.string().min(1)).min(1).optional(),
|
|
284
|
+
trusted_associations: z.array(z.enum(AUTHOR_ASSOCIATIONS)).min(1).optional(),
|
|
285
|
+
screen: z.boolean().optional(),
|
|
286
|
+
})
|
|
287
|
+
.strict();
|
|
247
288
|
export const nodeZ = z
|
|
248
289
|
.object({
|
|
249
290
|
name: z.string().min(1),
|
|
@@ -255,6 +296,8 @@ export const nodeZ = z
|
|
|
255
296
|
tools: nodeToolsZ.optional(),
|
|
256
297
|
fail_soft: z.boolean().optional(),
|
|
257
298
|
on_fail: z.enum(NODE_ON_FAIL).optional(),
|
|
299
|
+
permissions: nodePermissionsZ.optional(),
|
|
300
|
+
outputs: z.array(safeOutputDeclarationZ).min(1).optional(),
|
|
258
301
|
rules: nodeSourcesZ.optional(),
|
|
259
302
|
context: nodeSourcesZ.optional(),
|
|
260
303
|
eval: z.array(evaluatorZ).min(1).optional(),
|
|
@@ -307,6 +350,8 @@ export const workflowZ = z.object({
|
|
|
307
350
|
model: z.string().min(1).optional(),
|
|
308
351
|
judge_budget: z.number().int().min(0).optional(),
|
|
309
352
|
inputs: workflowInputsZ.optional(),
|
|
353
|
+
permissions: nodePermissionsZ.optional(),
|
|
354
|
+
safe_outputs: safeOutputsPolicyZ.optional(),
|
|
310
355
|
});
|
|
311
356
|
const LEGACY_VERIFY_MESSAGE = "uses the legacy 'verify:' field. It was renamed to 'eval:' in @sweny-ai/core v0.2.0 with a different shape (named evaluators with kind: value | function | judge). " +
|
|
312
357
|
"Migration guide: https://spec.sweny.ai/nodes/#eval";
|
|
@@ -548,6 +593,48 @@ export function validateWorkflow(workflow, knownSkills) {
|
|
|
548
593
|
});
|
|
549
594
|
}
|
|
550
595
|
}
|
|
596
|
+
// Permissions and safe outputs (#365). The workflow's `permissions` and
|
|
597
|
+
// `safe_outputs.allow` are a ceiling every node inherits.
|
|
598
|
+
const wfAccess = typeof workflow.permissions === "string" ? workflow.permissions : workflow.permissions?.access;
|
|
599
|
+
const allowedOutputs = workflow.safe_outputs?.allow;
|
|
600
|
+
for (const [nodeId, node] of Object.entries(workflow.nodes)) {
|
|
601
|
+
const nodeAccess = typeof node.permissions === "string" ? node.permissions : node.permissions?.access;
|
|
602
|
+
if (wfAccess === "read" && nodeAccess === "write") {
|
|
603
|
+
errors.push({
|
|
604
|
+
code: "PERMISSION_CEILING",
|
|
605
|
+
message: `Node "${nodeId}" asks for permissions write, above the workflow's permissions read`,
|
|
606
|
+
nodeId,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
const seenTypes = new Set();
|
|
610
|
+
for (const out of node.outputs ?? []) {
|
|
611
|
+
if (seenTypes.has(out.type)) {
|
|
612
|
+
errors.push({
|
|
613
|
+
code: "DUPLICATE_OUTPUT",
|
|
614
|
+
message: `Node "${nodeId}" declares output "${out.type}" more than once; merge them into one entry`,
|
|
615
|
+
nodeId,
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
seenTypes.add(out.type);
|
|
619
|
+
if (allowedOutputs && !allowedOutputs.includes(out.type)) {
|
|
620
|
+
errors.push({
|
|
621
|
+
code: "OUTPUT_NOT_ALLOWED",
|
|
622
|
+
message: `Node "${nodeId}" declares output "${out.type}", outside the workflow's safe_outputs.allow [${allowedOutputs.join(", ")}]`,
|
|
623
|
+
nodeId,
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
if (out.via && !SAFE_OUTPUT_APPLIERS[out.via]?.includes(out.type)) {
|
|
627
|
+
const supported = Object.entries(SAFE_OUTPUT_APPLIERS)
|
|
628
|
+
.filter(([, types]) => types.includes(out.type))
|
|
629
|
+
.map(([id]) => id);
|
|
630
|
+
errors.push({
|
|
631
|
+
code: "UNSUPPORTED_OUTPUT",
|
|
632
|
+
message: `Node "${nodeId}" output "${out.type}" names via "${out.via}", which cannot apply it (supported: ${supported.join(", ")})`,
|
|
633
|
+
nodeId,
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
551
638
|
// Skill references
|
|
552
639
|
if (knownSkills) {
|
|
553
640
|
// Merge workflow inline skills into known set
|
|
@@ -705,6 +792,71 @@ export const workflowJsonSchema = {
|
|
|
705
792
|
},
|
|
706
793
|
],
|
|
707
794
|
},
|
|
795
|
+
Permissions: {
|
|
796
|
+
description: "What a node's agent may do. 'read' runs it read-only: only access: read skill tools, no external skill MCP servers, no shell, file-write, edit, fetch or subagent built-ins. On the workflow it is the default and the ceiling for every node.",
|
|
797
|
+
oneOf: [
|
|
798
|
+
{ type: "string", enum: [...NODE_ACCESS] },
|
|
799
|
+
{
|
|
800
|
+
type: "object",
|
|
801
|
+
additionalProperties: false,
|
|
802
|
+
minProperties: 1,
|
|
803
|
+
properties: {
|
|
804
|
+
access: { type: "string", enum: [...NODE_ACCESS] },
|
|
805
|
+
deny: {
|
|
806
|
+
type: "array",
|
|
807
|
+
items: { type: "string", enum: [...TOOL_CLASSES] },
|
|
808
|
+
minItems: 1,
|
|
809
|
+
description: "Built-in tool classes the agent must not have, compiled per harness.",
|
|
810
|
+
},
|
|
811
|
+
strict: {
|
|
812
|
+
type: "boolean",
|
|
813
|
+
description: "Exclusive MCP (only the servers sweny injects), and refuse the node on a harness that cannot enforce the policy.",
|
|
814
|
+
},
|
|
815
|
+
},
|
|
816
|
+
},
|
|
817
|
+
],
|
|
818
|
+
},
|
|
819
|
+
SafeOutput: {
|
|
820
|
+
type: "object",
|
|
821
|
+
required: ["type"],
|
|
822
|
+
additionalProperties: false,
|
|
823
|
+
properties: {
|
|
824
|
+
type: { type: "string", enum: [...SAFE_OUTPUT_TYPES] },
|
|
825
|
+
via: {
|
|
826
|
+
type: "string",
|
|
827
|
+
minLength: 1,
|
|
828
|
+
description: "Skill that applies the write. Default: the first node skill that supports the type.",
|
|
829
|
+
},
|
|
830
|
+
max: {
|
|
831
|
+
type: "integer",
|
|
832
|
+
minimum: 1,
|
|
833
|
+
maximum: SAFE_OUTPUT_MAX_CEILING,
|
|
834
|
+
description: "Max writes of this type from this node per run. Default 1.",
|
|
835
|
+
},
|
|
836
|
+
target: {
|
|
837
|
+
type: "string",
|
|
838
|
+
minLength: 1,
|
|
839
|
+
description: "Pinned target (GitHub owner/repo, Linear team id). GitHub default: GITHUB_REPOSITORY.",
|
|
840
|
+
},
|
|
841
|
+
title_prefix: {
|
|
842
|
+
type: "string",
|
|
843
|
+
minLength: 1,
|
|
844
|
+
maxLength: 64,
|
|
845
|
+
description: "Prepended to issue and PR titles that do not already start with it.",
|
|
846
|
+
},
|
|
847
|
+
labels: {
|
|
848
|
+
type: "array",
|
|
849
|
+
items: { type: "string", minLength: 1 },
|
|
850
|
+
minItems: 1,
|
|
851
|
+
description: "issue / pr: labels always added. label: the only labels the agent may add.",
|
|
852
|
+
},
|
|
853
|
+
expires: {
|
|
854
|
+
type: "string",
|
|
855
|
+
pattern: SAFE_OUTPUT_EXPIRES_PATTERN.source,
|
|
856
|
+
description: "Drop an intent older than this when the write stage runs (e.g. 30m, 2h, 7d).",
|
|
857
|
+
},
|
|
858
|
+
},
|
|
859
|
+
},
|
|
708
860
|
},
|
|
709
861
|
properties: {
|
|
710
862
|
id: { type: "string", minLength: 1 },
|
|
@@ -739,6 +891,46 @@ export const workflowJsonSchema = {
|
|
|
739
891
|
minLength: 1,
|
|
740
892
|
description: "Default execution model for every node. Overridable per-node. Free-text passthrough (no registry); resolved as node.model ?? workflow.model ?? executor default.",
|
|
741
893
|
},
|
|
894
|
+
permissions: {
|
|
895
|
+
$ref: "#/$defs/Permissions",
|
|
896
|
+
description: "Default and ceiling for every node's permissions. Absent: write.",
|
|
897
|
+
},
|
|
898
|
+
safe_outputs: {
|
|
899
|
+
type: "object",
|
|
900
|
+
description: "Workflow-level safe-output policy: the ceiling on output types and run-wide limits.",
|
|
901
|
+
additionalProperties: false,
|
|
902
|
+
properties: {
|
|
903
|
+
allow: {
|
|
904
|
+
type: "array",
|
|
905
|
+
items: { type: "string", enum: [...SAFE_OUTPUT_TYPES] },
|
|
906
|
+
minItems: 1,
|
|
907
|
+
description: "Output types any node may declare. Absent: all types.",
|
|
908
|
+
},
|
|
909
|
+
max: {
|
|
910
|
+
type: "integer",
|
|
911
|
+
minimum: 1,
|
|
912
|
+
maximum: SAFE_OUTPUT_MAX_CEILING,
|
|
913
|
+
description: "Total writes per run across all nodes.",
|
|
914
|
+
},
|
|
915
|
+
staged: { type: "boolean", description: "Preview every write and apply none." },
|
|
916
|
+
trusted_actors: {
|
|
917
|
+
type: "array",
|
|
918
|
+
items: { type: "string", minLength: 1 },
|
|
919
|
+
minItems: 1,
|
|
920
|
+
description: "GitHub logins whose runs may write.",
|
|
921
|
+
},
|
|
922
|
+
trusted_associations: {
|
|
923
|
+
type: "array",
|
|
924
|
+
items: { type: "string", enum: [...AUTHOR_ASSOCIATIONS] },
|
|
925
|
+
minItems: 1,
|
|
926
|
+
description: "Author associations (from the GitHub event payload) whose runs may write.",
|
|
927
|
+
},
|
|
928
|
+
screen: {
|
|
929
|
+
type: "boolean",
|
|
930
|
+
description: "One model call that may veto the writes after every deterministic check. It can never authorize one.",
|
|
931
|
+
},
|
|
932
|
+
},
|
|
933
|
+
},
|
|
742
934
|
judge_budget: {
|
|
743
935
|
type: "integer",
|
|
744
936
|
minimum: 0,
|
|
@@ -860,11 +1052,11 @@ export const workflowJsonSchema = {
|
|
|
860
1052
|
disallowed_tools: {
|
|
861
1053
|
type: "array",
|
|
862
1054
|
items: { type: "string", minLength: 1 },
|
|
863
|
-
description: "Built-in tool names the agent cannot use at this node (
|
|
1055
|
+
description: "Built-in tool names the agent cannot use at this node, in the harness's own names (Claude Code: ['Bash']). Passed through to the harness: Claude Code removes them from the model context; Codex maps known names to tool classes and reports what it cannot deny as degraded. For portable workflows prefer tool classes in tools.deny.",
|
|
864
1056
|
},
|
|
865
1057
|
tools: {
|
|
866
1058
|
type: "object",
|
|
867
|
-
description: "Per-node filter over skill-provided tools. 'allow' keeps only the listed skill tools; 'deny' removes the listed skill tools (applied after 'allow'). Filtered tools are never registered for the node's run. Absent field = all skill tools exposed.
|
|
1059
|
+
description: "Per-node filter over skill-provided tools. 'allow' keeps only the listed skill tools; 'deny' removes the listed skill tools (applied after 'allow'). Filtered tools are never registered for the node's run. Absent field = all skill tools exposed. A 'deny' entry that names a portable tool class (shell, write, edit, net, subagent) also denies that class of built-in agent tools on every harness.",
|
|
868
1060
|
additionalProperties: false,
|
|
869
1061
|
anyOf: [{ required: ["allow"] }, { required: ["deny"] }],
|
|
870
1062
|
properties: {
|
|
@@ -877,6 +1069,7 @@ export const workflowJsonSchema = {
|
|
|
877
1069
|
type: "array",
|
|
878
1070
|
items: { type: "string", minLength: 1 },
|
|
879
1071
|
minItems: 1,
|
|
1072
|
+
description: "Skill tool names to remove, and/or portable tool classes (shell, write, edit, net, subagent) to deny for built-in agent tools. Each harness enforces a class natively or reports it as degraded; under strict harness policy an unenforceable class refuses the node.",
|
|
880
1073
|
},
|
|
881
1074
|
},
|
|
882
1075
|
},
|
|
@@ -889,6 +1082,16 @@ export const workflowJsonSchema = {
|
|
|
889
1082
|
enum: [...NODE_ON_FAIL],
|
|
890
1083
|
description: "What to do when this node finishes 'failed' (agent-level failure, or an eval failure that exhausted retries and was not softened by fail_soft). 'halt' (default) stops the workflow with the failure surfaced so a broken node never advances down a conditional edge; 'continue' preserves the legacy fall-through where routing proceeds from the failed node. Distinct from requires.on_fail (the pre-condition gate).",
|
|
891
1084
|
},
|
|
1085
|
+
permissions: {
|
|
1086
|
+
$ref: "#/$defs/Permissions",
|
|
1087
|
+
description: "What this node's agent may do. Absent: read when the node declares outputs, else the workflow's permissions, else write.",
|
|
1088
|
+
},
|
|
1089
|
+
outputs: {
|
|
1090
|
+
type: "array",
|
|
1091
|
+
items: { $ref: "#/$defs/SafeOutput" },
|
|
1092
|
+
minItems: 1,
|
|
1093
|
+
description: "Typed write intents this node may emit with the emit_output tool. sweny applies them after the node, within the declared caps.",
|
|
1094
|
+
},
|
|
892
1095
|
rules: {
|
|
893
1096
|
$ref: "#/$defs/NodeSources",
|
|
894
1097
|
description: "Per-node rules. Additive by default; set { only: true } to block cascade.",
|
|
@@ -981,7 +1184,10 @@ export const workflowJsonSchema = {
|
|
|
981
1184
|
properties: {
|
|
982
1185
|
from: { type: "string", minLength: 1 },
|
|
983
1186
|
to: { type: "string", minLength: 1 },
|
|
984
|
-
when: {
|
|
1187
|
+
when: {
|
|
1188
|
+
type: "string",
|
|
1189
|
+
description: "Natural language condition, evaluated at runtime by the workflow's harness.",
|
|
1190
|
+
},
|
|
985
1191
|
max_iterations: {
|
|
986
1192
|
type: "integer",
|
|
987
1193
|
minimum: 1,
|
package/dist/skills/github.js
CHANGED
|
@@ -229,6 +229,24 @@ export const github = {
|
|
|
229
229
|
return reused ? { ...pr, reused: true } : pr;
|
|
230
230
|
},
|
|
231
231
|
},
|
|
232
|
+
{
|
|
233
|
+
name: "github_add_labels",
|
|
234
|
+
access: "write",
|
|
235
|
+
description: "Add labels to a GitHub issue or pull request",
|
|
236
|
+
input_schema: {
|
|
237
|
+
type: "object",
|
|
238
|
+
properties: {
|
|
239
|
+
repo: { type: "string", description: "owner/repo" },
|
|
240
|
+
issue_number: { type: "number", description: "Issue or PR number" },
|
|
241
|
+
labels: { type: "array", items: { type: "string" }, description: "Labels to add" },
|
|
242
|
+
},
|
|
243
|
+
required: ["repo", "issue_number", "labels"],
|
|
244
|
+
},
|
|
245
|
+
handler: async (input, ctx) => gh(`/repos/${input.repo}/issues/${input.issue_number}/labels`, ctx, {
|
|
246
|
+
method: "POST",
|
|
247
|
+
body: JSON.stringify({ labels: input.labels }),
|
|
248
|
+
}),
|
|
249
|
+
},
|
|
232
250
|
{
|
|
233
251
|
name: "github_list_recent_commits",
|
|
234
252
|
access: "read",
|
package/dist/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type { Source, ResolvedSource, SourceKind, SourceResolutionMap } from "./
|
|
|
2
2
|
export type { WorkflowInputs, WorkflowInputField, WorkflowInputType, InputValidationError, InputValidationResult, } from "./inputs.js";
|
|
3
3
|
export { WORKFLOW_INPUT_TYPES } from "./inputs.js";
|
|
4
4
|
import type { Source as _Source, ResolvedSource as _ResolvedSource } from "./sources.js";
|
|
5
|
+
import type { ToolClass } from "./harness/types.js";
|
|
5
6
|
export type JSONSchema = Record<string, unknown>;
|
|
6
7
|
/** Context passed to tool handlers at execution time */
|
|
7
8
|
export interface ToolContext {
|
|
@@ -361,6 +362,93 @@ export interface Node {
|
|
|
361
362
|
* Distinct from `requires.on_fail`, which is the pre-condition gate.
|
|
362
363
|
*/
|
|
363
364
|
on_fail?: NodeOnFail;
|
|
365
|
+
/**
|
|
366
|
+
* What this node's agent may do (#365). `read` runs the node read-only:
|
|
367
|
+
* only `access: "read"` skill tools, no external skill MCP servers, no
|
|
368
|
+
* shell / file-write / edit / fetch / subagent built-ins. Absent: `read`
|
|
369
|
+
* when the node declares `outputs`, else the workflow's `permissions`,
|
|
370
|
+
* else `write` (today's behavior).
|
|
371
|
+
*/
|
|
372
|
+
permissions?: NodePermissions;
|
|
373
|
+
/**
|
|
374
|
+
* Typed write intents this node may emit (#365). The agent records them with
|
|
375
|
+
* the `emit_output` tool; sweny applies them after the node, within the
|
|
376
|
+
* declared caps. See {@link SafeOutputDeclaration}.
|
|
377
|
+
*/
|
|
378
|
+
outputs?: SafeOutputDeclaration[];
|
|
379
|
+
}
|
|
380
|
+
/** Node access level. `read` = no write-capable tool reaches the agent. */
|
|
381
|
+
export declare const NODE_ACCESS: readonly ["read", "write"];
|
|
382
|
+
export type NodeAccess = (typeof NODE_ACCESS)[number];
|
|
383
|
+
/** Portable built-in tool classes, compiled per harness (Claude Code: native tool names). */
|
|
384
|
+
export declare const TOOL_CLASSES: readonly ["shell", "write", "edit", "net", "subagent"];
|
|
385
|
+
/** Object form of {@link NodePermissions}. */
|
|
386
|
+
export interface NodePermissionsSpec {
|
|
387
|
+
access?: NodeAccess;
|
|
388
|
+
/** Built-in tool classes the agent must not have. */
|
|
389
|
+
deny?: (typeof TOOL_CLASSES)[number][];
|
|
390
|
+
/** Exclusive MCP, and refuse the node on a harness that cannot enforce the policy. */
|
|
391
|
+
strict?: boolean;
|
|
392
|
+
}
|
|
393
|
+
/** `read` / `write` shorthand, or the object form. */
|
|
394
|
+
export type NodePermissions = NodeAccess | NodePermissionsSpec;
|
|
395
|
+
/** Write operations a node may request through `emit_output`. */
|
|
396
|
+
export declare const SAFE_OUTPUT_TYPES: readonly ["comment", "issue", "pr", "label"];
|
|
397
|
+
export type SafeOutputType = (typeof SAFE_OUTPUT_TYPES)[number];
|
|
398
|
+
/**
|
|
399
|
+
* Skills that can apply each output type. The write stage calls the skill's
|
|
400
|
+
* own tool handler; a skill not listed here cannot apply safe outputs.
|
|
401
|
+
*/
|
|
402
|
+
export declare const SAFE_OUTPUT_APPLIERS: Readonly<Record<string, readonly SafeOutputType[]>>;
|
|
403
|
+
/** GitHub author associations accepted by `safe_outputs.trusted_associations`. */
|
|
404
|
+
export declare const AUTHOR_ASSOCIATIONS: readonly ["OWNER", "MEMBER", "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIME_CONTRIBUTOR", "FIRST_TIMER", "NONE"];
|
|
405
|
+
export type AuthorAssociation = (typeof AUTHOR_ASSOCIATIONS)[number];
|
|
406
|
+
/** Upper bound on a single output's `max` and on `safe_outputs.max`. */
|
|
407
|
+
export declare const SAFE_OUTPUT_MAX_CEILING = 100;
|
|
408
|
+
/** `expires` grammar: a positive integer and a unit (s, m, h, d). */
|
|
409
|
+
export declare const SAFE_OUTPUT_EXPIRES_PATTERN: RegExp;
|
|
410
|
+
/** One declared output on a node. */
|
|
411
|
+
export interface SafeOutputDeclaration {
|
|
412
|
+
type: SafeOutputType;
|
|
413
|
+
/** Skill that applies it. Default: the first node skill that supports the type. */
|
|
414
|
+
via?: string;
|
|
415
|
+
/** Max writes of this type from this node per run. Default 1. */
|
|
416
|
+
max?: number;
|
|
417
|
+
/** Pinned target (GitHub `owner/repo`, Linear team id). GitHub default: `GITHUB_REPOSITORY`. */
|
|
418
|
+
target?: string;
|
|
419
|
+
/** Prepended to issue / PR titles that do not already start with it. */
|
|
420
|
+
title_prefix?: string;
|
|
421
|
+
/** issue / pr: labels always added. label: the only labels the agent may add. */
|
|
422
|
+
labels?: string[];
|
|
423
|
+
/** Drop an intent older than this when the write stage runs (e.g. `30m`, `2h`, `7d`). */
|
|
424
|
+
expires?: string;
|
|
425
|
+
}
|
|
426
|
+
/** Workflow-level safe-output policy: the ceiling and run-wide limits. */
|
|
427
|
+
export interface SafeOutputsPolicy {
|
|
428
|
+
/** Output types any node may declare. Absent: all types. */
|
|
429
|
+
allow?: SafeOutputType[];
|
|
430
|
+
/** Total writes per run across all nodes. */
|
|
431
|
+
max?: number;
|
|
432
|
+
/** Preview every write and apply none. */
|
|
433
|
+
staged?: boolean;
|
|
434
|
+
/** GitHub logins whose runs may write. */
|
|
435
|
+
trusted_actors?: string[];
|
|
436
|
+
/** Author associations whose runs may write (from the GitHub event payload). */
|
|
437
|
+
trusted_associations?: AuthorAssociation[];
|
|
438
|
+
/** One model call that may veto the writes. It can never authorize one. */
|
|
439
|
+
screen?: boolean;
|
|
440
|
+
}
|
|
441
|
+
/** What the write stage did with one intent. Metadata only: never a title or body. */
|
|
442
|
+
export interface SafeOutputReceipt {
|
|
443
|
+
type: string;
|
|
444
|
+
/** Skill that applied (or would apply) it. */
|
|
445
|
+
via?: string;
|
|
446
|
+
status: "applied" | "staged" | "skipped" | "refused" | "vetoed" | "failed";
|
|
447
|
+
/** Why it was skipped, refused, vetoed or failed. Fixed wording, never model text. */
|
|
448
|
+
reason?: string;
|
|
449
|
+
target?: string;
|
|
450
|
+
/** Issue / PR / comment number or id the write produced. */
|
|
451
|
+
ref?: string | number;
|
|
364
452
|
}
|
|
365
453
|
/**
|
|
366
454
|
* Per-node skill-tool filter. See {@link Node.tools}.
|
|
@@ -376,7 +464,7 @@ export interface NodeToolFilter {
|
|
|
376
464
|
export interface Edge {
|
|
377
465
|
from: string;
|
|
378
466
|
to: string;
|
|
379
|
-
/** Natural language condition
|
|
467
|
+
/** Natural language condition, evaluated at runtime by the workflow's harness. */
|
|
380
468
|
when?: string;
|
|
381
469
|
/** Max times this edge can be followed (enables retry loops). Default: unlimited. */
|
|
382
470
|
max_iterations?: number;
|
|
@@ -423,6 +511,10 @@ export interface Workflow {
|
|
|
423
511
|
* See `src/inputs.ts` for the field shape and validation rules.
|
|
424
512
|
*/
|
|
425
513
|
inputs?: import("./inputs.js").WorkflowInputs;
|
|
514
|
+
/** Default and ceiling for every node's `permissions` (#365). Absent: `write`. */
|
|
515
|
+
permissions?: NodePermissions;
|
|
516
|
+
/** Workflow-level safe-output policy (#365). */
|
|
517
|
+
safe_outputs?: SafeOutputsPolicy;
|
|
426
518
|
}
|
|
427
519
|
/**
|
|
428
520
|
* Inline skill definition in a workflow's `skills` block.
|
|
@@ -489,6 +581,8 @@ export interface NodeResult {
|
|
|
489
581
|
};
|
|
490
582
|
/** Opinions this run could not honor natively. Always empty for Claude Code. */
|
|
491
583
|
degraded?: string[];
|
|
584
|
+
/** Safe outputs (#365): what the write stage did with each intent. Absent when the node declares none. */
|
|
585
|
+
outputs?: SafeOutputReceipt[];
|
|
492
586
|
}
|
|
493
587
|
export interface ToolCall {
|
|
494
588
|
tool: string;
|
|
@@ -624,6 +718,12 @@ export interface Claude {
|
|
|
624
718
|
maxTurns?: number;
|
|
625
719
|
/** Built-in SDK tool names to disallow for this node (e.g. ["Bash"]). */
|
|
626
720
|
disallowedTools?: string[];
|
|
721
|
+
/**
|
|
722
|
+
* Portable built-in tool classes denied at this node (`tools.deny` entries
|
|
723
|
+
* that name a class: shell, write, edit, net, subagent). Each harness
|
|
724
|
+
* compiles them to its native form, or reports them in `degraded`.
|
|
725
|
+
*/
|
|
726
|
+
deny?: ToolClass[];
|
|
627
727
|
/** Per-node execution model. Overrides the client default when set. Absent = no SDK model option emitted. */
|
|
628
728
|
model?: string;
|
|
629
729
|
/** Abort the query after this many ms. Default: no timeout (back-compat). */
|
|
@@ -645,6 +745,12 @@ export interface Claude {
|
|
|
645
745
|
* (external MCP servers, shell, file-edit built-ins).
|
|
646
746
|
*/
|
|
647
747
|
readOnly?: boolean;
|
|
748
|
+
/**
|
|
749
|
+
* The node's portable policy (#365), compiled per adapter. Legacy `Claude`
|
|
750
|
+
* implementations may ignore it; `readOnly` and `disallowedTools` above
|
|
751
|
+
* carry the same read-only and native-deny intent.
|
|
752
|
+
*/
|
|
753
|
+
policy?: import("./harness/types.js").NodePolicy;
|
|
648
754
|
}): Promise<NodeResult>;
|
|
649
755
|
/**
|
|
650
756
|
* Evaluate a routing condition — pick one of N choices.
|
package/dist/types.js
CHANGED
|
@@ -67,6 +67,35 @@ export const REQUIRES_ON_FAIL = ["fail", "skip"];
|
|
|
67
67
|
export const NODE_ON_FAIL = ["halt", "continue"];
|
|
68
68
|
/** MCP server transport type. Inferred from command/url when omitted. */
|
|
69
69
|
export const MCP_TRANSPORTS = ["stdio", "http"];
|
|
70
|
+
// ─── Permissions and safe outputs (#365) ─────────────────────────
|
|
71
|
+
/** Node access level. `read` = no write-capable tool reaches the agent. */
|
|
72
|
+
export const NODE_ACCESS = ["read", "write"];
|
|
73
|
+
/** Portable built-in tool classes, compiled per harness (Claude Code: native tool names). */
|
|
74
|
+
export const TOOL_CLASSES = ["shell", "write", "edit", "net", "subagent"];
|
|
75
|
+
/** Write operations a node may request through `emit_output`. */
|
|
76
|
+
export const SAFE_OUTPUT_TYPES = ["comment", "issue", "pr", "label"];
|
|
77
|
+
/**
|
|
78
|
+
* Skills that can apply each output type. The write stage calls the skill's
|
|
79
|
+
* own tool handler; a skill not listed here cannot apply safe outputs.
|
|
80
|
+
*/
|
|
81
|
+
export const SAFE_OUTPUT_APPLIERS = {
|
|
82
|
+
github: ["comment", "issue", "pr", "label"],
|
|
83
|
+
linear: ["comment", "issue"],
|
|
84
|
+
};
|
|
85
|
+
/** GitHub author associations accepted by `safe_outputs.trusted_associations`. */
|
|
86
|
+
export const AUTHOR_ASSOCIATIONS = [
|
|
87
|
+
"OWNER",
|
|
88
|
+
"MEMBER",
|
|
89
|
+
"COLLABORATOR",
|
|
90
|
+
"CONTRIBUTOR",
|
|
91
|
+
"FIRST_TIME_CONTRIBUTOR",
|
|
92
|
+
"FIRST_TIMER",
|
|
93
|
+
"NONE",
|
|
94
|
+
];
|
|
95
|
+
/** Upper bound on a single output's `max` and on `safe_outputs.max`. */
|
|
96
|
+
export const SAFE_OUTPUT_MAX_CEILING = 100;
|
|
97
|
+
/** `expires` grammar: a positive integer and a unit (s, m, h, d). */
|
|
98
|
+
export const SAFE_OUTPUT_EXPIRES_PATTERN = /^[1-9][0-9]*(s|m|h|d)$/;
|
|
70
99
|
/**
|
|
71
100
|
* Workflow type discriminator. Cloud uses this to render runs in a
|
|
72
101
|
* type-specific way. Adding a value requires a corresponding renderer.
|