@sweny-ai/core 0.1.157 → 0.1.159

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/schema.d.ts CHANGED
@@ -848,6 +848,26 @@ export declare const nodePermissionsZ: z.ZodUnion<[z.ZodEnum<["read", "write"]>,
848
848
  deny?: ("shell" | "write" | "edit" | "net" | "subagent")[] | undefined;
849
849
  access?: "write" | "read" | undefined;
850
850
  }>]>;
851
+ /**
852
+ * Spend ceiling (#449): input + output tokens and/or reported USD. The object
853
+ * must declare at least one of the two.
854
+ */
855
+ export declare const budgetZ: z.ZodEffects<z.ZodObject<{
856
+ tokens: z.ZodOptional<z.ZodNumber>;
857
+ cost_usd: z.ZodOptional<z.ZodNumber>;
858
+ }, "strict", z.ZodTypeAny, {
859
+ tokens?: number | undefined;
860
+ cost_usd?: number | undefined;
861
+ }, {
862
+ tokens?: number | undefined;
863
+ cost_usd?: number | undefined;
864
+ }>, {
865
+ tokens?: number | undefined;
866
+ cost_usd?: number | undefined;
867
+ }, {
868
+ tokens?: number | undefined;
869
+ cost_usd?: number | undefined;
870
+ }>;
851
871
  /** One typed write intent a node may emit (#365). */
852
872
  export declare const safeOutputDeclarationZ: z.ZodObject<{
853
873
  type: z.ZodEnum<["comment", "issue", "pr", "label", "issue_state"]>;
@@ -940,6 +960,22 @@ export declare const nodeZ: z.ZodObject<{
940
960
  skills: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
941
961
  output: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
942
962
  max_turns: z.ZodOptional<z.ZodNumber>;
963
+ budget: z.ZodOptional<z.ZodEffects<z.ZodObject<{
964
+ tokens: z.ZodOptional<z.ZodNumber>;
965
+ cost_usd: z.ZodOptional<z.ZodNumber>;
966
+ }, "strict", z.ZodTypeAny, {
967
+ tokens?: number | undefined;
968
+ cost_usd?: number | undefined;
969
+ }, {
970
+ tokens?: number | undefined;
971
+ cost_usd?: number | undefined;
972
+ }>, {
973
+ tokens?: number | undefined;
974
+ cost_usd?: number | undefined;
975
+ }, {
976
+ tokens?: number | undefined;
977
+ cost_usd?: number | undefined;
978
+ }>>;
943
979
  disallowed_tools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
944
980
  tools: z.ZodOptional<z.ZodEffects<z.ZodObject<{
945
981
  allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
@@ -1426,6 +1462,10 @@ export declare const nodeZ: z.ZodObject<{
1426
1462
  model?: string | undefined;
1427
1463
  on_fail?: "halt" | "continue" | undefined;
1428
1464
  max_turns?: number | undefined;
1465
+ budget?: {
1466
+ tokens?: number | undefined;
1467
+ cost_usd?: number | undefined;
1468
+ } | undefined;
1429
1469
  disallowed_tools?: string[] | undefined;
1430
1470
  fail_soft?: boolean | undefined;
1431
1471
  permissions?: "write" | "read" | {
@@ -1540,6 +1580,10 @@ export declare const nodeZ: z.ZodObject<{
1540
1580
  on_fail?: "halt" | "continue" | undefined;
1541
1581
  skills?: string[] | undefined;
1542
1582
  max_turns?: number | undefined;
1583
+ budget?: {
1584
+ tokens?: number | undefined;
1585
+ cost_usd?: number | undefined;
1586
+ } | undefined;
1543
1587
  disallowed_tools?: string[] | undefined;
1544
1588
  fail_soft?: boolean | undefined;
1545
1589
  permissions?: "write" | "read" | {
@@ -1698,6 +1742,22 @@ export declare const workflowZ: z.ZodObject<{
1698
1742
  skills: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
1699
1743
  output: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1700
1744
  max_turns: z.ZodOptional<z.ZodNumber>;
1745
+ budget: z.ZodOptional<z.ZodEffects<z.ZodObject<{
1746
+ tokens: z.ZodOptional<z.ZodNumber>;
1747
+ cost_usd: z.ZodOptional<z.ZodNumber>;
1748
+ }, "strict", z.ZodTypeAny, {
1749
+ tokens?: number | undefined;
1750
+ cost_usd?: number | undefined;
1751
+ }, {
1752
+ tokens?: number | undefined;
1753
+ cost_usd?: number | undefined;
1754
+ }>, {
1755
+ tokens?: number | undefined;
1756
+ cost_usd?: number | undefined;
1757
+ }, {
1758
+ tokens?: number | undefined;
1759
+ cost_usd?: number | undefined;
1760
+ }>>;
1701
1761
  disallowed_tools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
1702
1762
  tools: z.ZodOptional<z.ZodEffects<z.ZodObject<{
1703
1763
  allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
@@ -2184,6 +2244,10 @@ export declare const workflowZ: z.ZodObject<{
2184
2244
  model?: string | undefined;
2185
2245
  on_fail?: "halt" | "continue" | undefined;
2186
2246
  max_turns?: number | undefined;
2247
+ budget?: {
2248
+ tokens?: number | undefined;
2249
+ cost_usd?: number | undefined;
2250
+ } | undefined;
2187
2251
  disallowed_tools?: string[] | undefined;
2188
2252
  fail_soft?: boolean | undefined;
2189
2253
  permissions?: "write" | "read" | {
@@ -2298,6 +2362,10 @@ export declare const workflowZ: z.ZodObject<{
2298
2362
  on_fail?: "halt" | "continue" | undefined;
2299
2363
  skills?: string[] | undefined;
2300
2364
  max_turns?: number | undefined;
2365
+ budget?: {
2366
+ tokens?: number | undefined;
2367
+ cost_usd?: number | undefined;
2368
+ } | undefined;
2301
2369
  disallowed_tools?: string[] | undefined;
2302
2370
  fail_soft?: boolean | undefined;
2303
2371
  permissions?: "write" | "read" | {
@@ -2547,6 +2615,22 @@ export declare const workflowZ: z.ZodObject<{
2547
2615
  judge_model: z.ZodOptional<z.ZodString>;
2548
2616
  model: z.ZodOptional<z.ZodString>;
2549
2617
  judge_budget: z.ZodOptional<z.ZodNumber>;
2618
+ budget: z.ZodOptional<z.ZodEffects<z.ZodObject<{
2619
+ tokens: z.ZodOptional<z.ZodNumber>;
2620
+ cost_usd: z.ZodOptional<z.ZodNumber>;
2621
+ }, "strict", z.ZodTypeAny, {
2622
+ tokens?: number | undefined;
2623
+ cost_usd?: number | undefined;
2624
+ }, {
2625
+ tokens?: number | undefined;
2626
+ cost_usd?: number | undefined;
2627
+ }>, {
2628
+ tokens?: number | undefined;
2629
+ cost_usd?: number | undefined;
2630
+ }, {
2631
+ tokens?: number | undefined;
2632
+ cost_usd?: number | undefined;
2633
+ }>>;
2550
2634
  context_mode: z.ZodOptional<z.ZodEnum<["bounded", "full"]>>;
2551
2635
  inputs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
2552
2636
  type: z.ZodEnum<["string", "number", "boolean", "string[]"]>;
@@ -2658,6 +2742,10 @@ export declare const workflowZ: z.ZodObject<{
2658
2742
  model?: string | undefined;
2659
2743
  on_fail?: "halt" | "continue" | undefined;
2660
2744
  max_turns?: number | undefined;
2745
+ budget?: {
2746
+ tokens?: number | undefined;
2747
+ cost_usd?: number | undefined;
2748
+ } | undefined;
2661
2749
  disallowed_tools?: string[] | undefined;
2662
2750
  fail_soft?: boolean | undefined;
2663
2751
  permissions?: "write" | "read" | {
@@ -2762,6 +2850,10 @@ export declare const workflowZ: z.ZodObject<{
2762
2850
  }[];
2763
2851
  entry: string;
2764
2852
  model?: string | undefined;
2853
+ budget?: {
2854
+ tokens?: number | undefined;
2855
+ cost_usd?: number | undefined;
2856
+ } | undefined;
2765
2857
  permissions?: "write" | "read" | {
2766
2858
  strict?: boolean | undefined;
2767
2859
  deny?: ("shell" | "write" | "edit" | "net" | "subagent")[] | undefined;
@@ -2824,6 +2916,10 @@ export declare const workflowZ: z.ZodObject<{
2824
2916
  on_fail?: "halt" | "continue" | undefined;
2825
2917
  skills?: string[] | undefined;
2826
2918
  max_turns?: number | undefined;
2919
+ budget?: {
2920
+ tokens?: number | undefined;
2921
+ cost_usd?: number | undefined;
2922
+ } | undefined;
2827
2923
  disallowed_tools?: string[] | undefined;
2828
2924
  fail_soft?: boolean | undefined;
2829
2925
  permissions?: "write" | "read" | {
@@ -2942,6 +3038,10 @@ export declare const workflowZ: z.ZodObject<{
2942
3038
  env?: Record<string, string> | undefined;
2943
3039
  } | undefined;
2944
3040
  }> | undefined;
3041
+ budget?: {
3042
+ tokens?: number | undefined;
3043
+ cost_usd?: number | undefined;
3044
+ } | undefined;
2945
3045
  permissions?: "write" | "read" | {
2946
3046
  strict?: boolean | undefined;
2947
3047
  deny?: ("shell" | "write" | "edit" | "net" | "subagent")[] | undefined;
@@ -3032,6 +3132,10 @@ export declare function parseWorkflow(raw: unknown): {
3032
3132
  model?: string | undefined;
3033
3133
  on_fail?: "halt" | "continue" | undefined;
3034
3134
  max_turns?: number | undefined;
3135
+ budget?: {
3136
+ tokens?: number | undefined;
3137
+ cost_usd?: number | undefined;
3138
+ } | undefined;
3035
3139
  disallowed_tools?: string[] | undefined;
3036
3140
  fail_soft?: boolean | undefined;
3037
3141
  permissions?: "write" | "read" | {
@@ -3136,6 +3240,10 @@ export declare function parseWorkflow(raw: unknown): {
3136
3240
  }[];
3137
3241
  entry: string;
3138
3242
  model?: string | undefined;
3243
+ budget?: {
3244
+ tokens?: number | undefined;
3245
+ cost_usd?: number | undefined;
3246
+ } | undefined;
3139
3247
  permissions?: "write" | "read" | {
3140
3248
  strict?: boolean | undefined;
3141
3249
  deny?: ("shell" | "write" | "edit" | "net" | "subagent")[] | undefined;
@@ -3178,7 +3286,7 @@ export declare function parseWorkflow(raw: unknown): {
3178
3286
  } | undefined;
3179
3287
  };
3180
3288
  export interface WorkflowError {
3181
- code: "MISSING_ENTRY" | "UNKNOWN_EDGE_SOURCE" | "UNKNOWN_EDGE_TARGET" | "UNREACHABLE_NODE" | "UNKNOWN_SKILL" | "SELF_LOOP" | "UNBOUNDED_CYCLE" | "AMBIGUOUS_EDGES" | "UNSUPPORTED_EVAL_POLICY" | "INVALID_INLINE_SKILL" | "EDGE_ITERATIONS_EXCEEDED" | "RETRY_MAX_EXCEEDED" | "PERMISSION_CEILING" | "OUTPUT_NOT_ALLOWED" | "DUPLICATE_OUTPUT" | "UNSUPPORTED_OUTPUT";
3289
+ code: "MISSING_ENTRY" | "UNKNOWN_EDGE_SOURCE" | "UNKNOWN_EDGE_TARGET" | "UNREACHABLE_NODE" | "UNKNOWN_SKILL" | "SELF_LOOP" | "UNBOUNDED_CYCLE" | "AMBIGUOUS_EDGES" | "UNSUPPORTED_EVAL_POLICY" | "INVALID_INLINE_SKILL" | "EDGE_ITERATIONS_EXCEEDED" | "RETRY_MAX_EXCEEDED" | "PERMISSION_CEILING" | "BUDGET_CEILING" | "OUTPUT_NOT_ALLOWED" | "DUPLICATE_OUTPUT" | "UNSUPPORTED_OUTPUT";
3182
3290
  message: string;
3183
3291
  nodeId?: string;
3184
3292
  }
@@ -3438,6 +3546,24 @@ export declare const workflowJsonSchema: {
3438
3546
  };
3439
3547
  }];
3440
3548
  };
3549
+ readonly Budget: {
3550
+ readonly type: "object";
3551
+ readonly additionalProperties: false;
3552
+ readonly minProperties: 1;
3553
+ readonly description: "A spend ceiling. 'tokens' counts input plus output tokens as the harness reports them; 'cost_usd' is the harness-reported cost (never estimated). A crossing stops the agent and fails the node; the run halts.";
3554
+ readonly properties: {
3555
+ readonly tokens: {
3556
+ readonly type: "integer";
3557
+ readonly minimum: 1;
3558
+ readonly description: "Max input plus output tokens.";
3559
+ };
3560
+ readonly cost_usd: {
3561
+ readonly type: "number";
3562
+ readonly exclusiveMinimum: 0;
3563
+ readonly description: "Max reported cost in USD.";
3564
+ };
3565
+ };
3566
+ };
3441
3567
  readonly SafeOutput: {
3442
3568
  readonly type: "object";
3443
3569
  readonly required: readonly ["type"];
@@ -3614,6 +3740,10 @@ export declare const workflowJsonSchema: {
3614
3740
  readonly minimum: 0;
3615
3741
  readonly description: "Soft cap on expected judge calls per workflow run. Executor warns at load time if exceeded; not a hard runtime cap in v1.";
3616
3742
  };
3743
+ readonly budget: {
3744
+ readonly $ref: "#/$defs/Budget";
3745
+ readonly description: "Spend ceiling for the whole run and for every node. A node's own budget may only narrow it. The CLI's --max-tokens and --max-cost tighten it further.";
3746
+ };
3617
3747
  readonly context_mode: {
3618
3748
  readonly type: "string";
3619
3749
  readonly enum: readonly ["bounded", "full"];
@@ -3779,6 +3909,10 @@ export declare const workflowJsonSchema: {
3779
3909
  readonly minimum: 1;
3780
3910
  readonly description: "Max AI model turns for this node. When absent, the executor's default applies.";
3781
3911
  };
3912
+ readonly budget: {
3913
+ readonly $ref: "#/$defs/Budget";
3914
+ readonly description: "Spend ceiling for one visit to this node, retry attempts included. Never above the workflow's budget. Crossing it stops the agent and fails the node (fail_soft and on_fail: continue do not apply) and the run halts.";
3915
+ };
3782
3916
  readonly disallowed_tools: {
3783
3917
  readonly type: "array";
3784
3918
  readonly items: {
package/dist/schema.js CHANGED
@@ -262,6 +262,19 @@ export const nodePermissionsZ = z.union([
262
262
  message: "permissions must declare at least one of access, deny, strict",
263
263
  }),
264
264
  ]);
265
+ /**
266
+ * Spend ceiling (#449): input + output tokens and/or reported USD. The object
267
+ * must declare at least one of the two.
268
+ */
269
+ export const budgetZ = z
270
+ .object({
271
+ tokens: z.number().int().min(1).optional(),
272
+ cost_usd: z.number().positive().optional(),
273
+ })
274
+ .strict()
275
+ .refine((b) => b.tokens !== undefined || b.cost_usd !== undefined, {
276
+ message: "budget must declare at least one of tokens, cost_usd",
277
+ });
265
278
  /** One typed write intent a node may emit (#365). */
266
279
  export const safeOutputDeclarationZ = z
267
280
  .object({
@@ -296,6 +309,7 @@ export const nodeZ = z
296
309
  skills: z.array(z.string()).default([]),
297
310
  output: jsonSchemaZ.optional(),
298
311
  max_turns: z.number().int().min(1).optional(),
312
+ budget: budgetZ.optional(),
299
313
  disallowed_tools: z.array(z.string().min(1)).optional(),
300
314
  tools: nodeToolsZ.optional(),
301
315
  fail_soft: z.boolean().optional(),
@@ -353,6 +367,7 @@ export const workflowZ = z.object({
353
367
  judge_model: z.string().min(1).optional(),
354
368
  model: z.string().min(1).optional(),
355
369
  judge_budget: z.number().int().min(0).optional(),
370
+ budget: budgetZ.optional(),
356
371
  context_mode: z.enum(CONTEXT_MODES).optional(),
357
372
  inputs: workflowInputsZ.optional(),
358
373
  permissions: nodePermissionsZ.optional(),
@@ -611,6 +626,27 @@ export function validateWorkflow(workflow, knownSkills) {
611
626
  nodeId,
612
627
  });
613
628
  }
629
+ // Budgets (#449): the workflow's `budget` is the ceiling for every node.
630
+ const wfBudget = workflow.budget;
631
+ const nodeBudget = node.budget;
632
+ if (wfBudget && nodeBudget) {
633
+ if (wfBudget.tokens !== undefined && nodeBudget.tokens !== undefined && nodeBudget.tokens > wfBudget.tokens) {
634
+ errors.push({
635
+ code: "BUDGET_CEILING",
636
+ message: `Node "${nodeId}" declares budget.tokens ${nodeBudget.tokens}, above the workflow's budget.tokens ${wfBudget.tokens}`,
637
+ nodeId,
638
+ });
639
+ }
640
+ if (wfBudget.cost_usd !== undefined &&
641
+ nodeBudget.cost_usd !== undefined &&
642
+ nodeBudget.cost_usd > wfBudget.cost_usd) {
643
+ errors.push({
644
+ code: "BUDGET_CEILING",
645
+ message: `Node "${nodeId}" declares budget.cost_usd ${nodeBudget.cost_usd}, above the workflow's budget.cost_usd ${wfBudget.cost_usd}`,
646
+ nodeId,
647
+ });
648
+ }
649
+ }
614
650
  const seenTypes = new Set();
615
651
  for (const out of node.outputs ?? []) {
616
652
  if (seenTypes.has(out.type)) {
@@ -844,6 +880,16 @@ export const workflowJsonSchema = {
844
880
  },
845
881
  ],
846
882
  },
883
+ Budget: {
884
+ type: "object",
885
+ additionalProperties: false,
886
+ minProperties: 1,
887
+ description: "A spend ceiling. 'tokens' counts input plus output tokens as the harness reports them; 'cost_usd' is the harness-reported cost (never estimated). A crossing stops the agent and fails the node; the run halts.",
888
+ properties: {
889
+ tokens: { type: "integer", minimum: 1, description: "Max input plus output tokens." },
890
+ cost_usd: { type: "number", exclusiveMinimum: 0, description: "Max reported cost in USD." },
891
+ },
892
+ },
847
893
  SafeOutput: {
848
894
  type: "object",
849
895
  required: ["type"],
@@ -984,6 +1030,10 @@ export const workflowJsonSchema = {
984
1030
  // applies the soft-cap default at use-time. See judge_model above.
985
1031
  description: "Soft cap on expected judge calls per workflow run. Executor warns at load time if exceeded; not a hard runtime cap in v1.",
986
1032
  },
1033
+ budget: {
1034
+ $ref: "#/$defs/Budget",
1035
+ description: "Spend ceiling for the whole run and for every node. A node's own budget may only narrow it. The CLI's --max-tokens and --max-cost tighten it further.",
1036
+ },
987
1037
  context_mode: {
988
1038
  type: "string",
989
1039
  enum: [...CONTEXT_MODES],
@@ -1100,6 +1150,10 @@ export const workflowJsonSchema = {
1100
1150
  minimum: 1,
1101
1151
  description: "Max AI model turns for this node. When absent, the executor's default applies.",
1102
1152
  },
1153
+ budget: {
1154
+ $ref: "#/$defs/Budget",
1155
+ description: "Spend ceiling for one visit to this node, retry attempts included. Never above the workflow's budget. Crossing it stops the agent and fails the node (fail_soft and on_fail: continue do not apply) and the run halts.",
1156
+ },
1103
1157
  disallowed_tools: {
1104
1158
  type: "array",
1105
1159
  items: { type: "string", minLength: 1 },
package/dist/types.d.ts CHANGED
@@ -3,6 +3,7 @@ export type { WorkflowInputs, WorkflowInputField, WorkflowInputType, InputValida
3
3
  export { WORKFLOW_INPUT_TYPES } from "./inputs.js";
4
4
  import type { Source as _Source, ResolvedSource as _ResolvedSource } from "./sources.js";
5
5
  import type { ToolClass } from "./harness/types.js";
6
+ import type { Budget, BudgetOverrun } from "./budget.js";
6
7
  export type JSONSchema = Record<string, unknown>;
7
8
  /** Context passed to tool handlers at execution time */
8
9
  export interface ToolContext {
@@ -294,6 +295,12 @@ export interface Node {
294
295
  output?: JSONSchema;
295
296
  /** Max AI model turns for this node. When absent, the executor's default applies. */
296
297
  max_turns?: number;
298
+ /**
299
+ * Spend ceiling for one visit to this node (all retry attempts included),
300
+ * in input + output tokens and/or reported USD. Never above the workflow's
301
+ * `budget`. A crossing stops the agent and fails the node (see `budget.ts`).
302
+ */
303
+ budget?: Budget;
297
304
  /** Per-node directives. Additive by default; set `{ only: true, sources: [...] }` to block cascade. */
298
305
  rules?: NodeSources;
299
306
  /** Per-node background knowledge. Additive by default; set `{ only: true, sources: [...] }` to block cascade. */
@@ -523,6 +530,11 @@ export interface Workflow {
523
530
  model?: string;
524
531
  /** Soft cap on expected judge calls per workflow run. Warning at load time when exceeded. */
525
532
  judge_budget?: number;
533
+ /**
534
+ * Spend ceiling for the whole run and for every node (#449). A node's own
535
+ * `budget` may only narrow it. The CLI's `--max-tokens` / `--max-cost` tighten it.
536
+ */
537
+ budget?: Budget;
526
538
  /**
527
539
  * What prior results a node's prompt receives (#337). `bounded` (default):
528
540
  * only nodes it can depend on (graph ancestors, nodes named by `requires`
@@ -612,6 +624,12 @@ export interface NodeResult {
612
624
  degraded?: string[];
613
625
  /** Safe outputs (#365): what the write stage did with each intent. Absent when the node declares none. */
614
626
  outputs?: SafeOutputReceipt[];
627
+ /**
628
+ * Set when this node's spend crossed a token or cost budget (#449), or when
629
+ * the run's budget was already spent before it could start. The node is
630
+ * `failed`, `fail_soft` and `on_fail: continue` do not apply, and the run halts.
631
+ */
632
+ budget?: BudgetOverrun;
615
633
  }
616
634
  export interface ToolCall {
617
635
  tool: string;
@@ -780,6 +798,13 @@ export interface Claude {
780
798
  * carry the same read-only and native-deny intent.
781
799
  */
782
800
  policy?: import("./harness/types.js").NodePolicy;
801
+ /**
802
+ * Live spend (#449): a harness that declares `capabilities.usage.live`
803
+ * calls this with the run's cumulative usage as it grows. The executor
804
+ * aborts `signal` when a budget is crossed. Harnesses that report usage
805
+ * only at the end never call it.
806
+ */
807
+ onUsage?: (usage: NodeUsage) => void;
783
808
  }): Promise<NodeResult>;
784
809
  /**
785
810
  * Evaluate a routing condition — pick one of N choices.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sweny-ai/core",
3
- "version": "0.1.157",
3
+ "version": "0.1.159",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sweny": "./dist/cli/main.js"