@sweny-ai/core 0.1.76 → 0.1.77
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/__tests__/cascade.test.js +3 -0
- package/dist/__tests__/executor.test.js +899 -0
- package/dist/__tests__/requires.test.d.ts +1 -0
- package/dist/__tests__/requires.test.js +82 -0
- package/dist/__tests__/retry.test.d.ts +1 -0
- package/dist/__tests__/retry.test.js +213 -0
- package/dist/__tests__/schema.test.js +75 -1
- package/dist/__tests__/spec-conformance.test.js +10 -0
- package/dist/__tests__/testing.test.js +18 -0
- package/dist/claude.d.ts +4 -0
- package/dist/claude.js +41 -0
- package/dist/executor.js +104 -43
- package/dist/executor.test.js +15 -0
- package/dist/requires.d.ts +7 -0
- package/dist/requires.js +34 -0
- package/dist/retry.d.ts +23 -0
- package/dist/retry.js +72 -0
- package/dist/schema.d.ts +603 -48
- package/dist/schema.js +97 -0
- package/dist/testing.d.ts +7 -0
- package/dist/testing.js +7 -0
- package/dist/types.d.ts +64 -0
- package/dist/verify.d.ts +1 -0
- package/dist/verify.js +1 -1
- package/dist/workflow-builder.test.js +6 -0
- package/package.json +1 -1
package/dist/schema.js
CHANGED
|
@@ -101,6 +101,21 @@ export const nodeVerifyZ = z
|
|
|
101
101
|
v.output_matches !== undefined, {
|
|
102
102
|
message: "verify must declare at least one check (any_tool_called, all_tools_called, no_tool_called, output_required, or output_matches)",
|
|
103
103
|
});
|
|
104
|
+
export const nodeRequiresZ = z
|
|
105
|
+
.object({
|
|
106
|
+
output_required: z.array(z.string().min(1)).min(1).optional(),
|
|
107
|
+
output_matches: z.array(outputMatchZ).min(1).optional(),
|
|
108
|
+
on_fail: z.enum(["fail", "skip"]).optional(),
|
|
109
|
+
})
|
|
110
|
+
.refine((r) => r.output_required !== undefined || r.output_matches !== undefined, {
|
|
111
|
+
message: "requires must declare at least one check (output_required or output_matches)",
|
|
112
|
+
});
|
|
113
|
+
const retryInstructionAutoZ = z.object({ auto: z.literal(true) }).strict();
|
|
114
|
+
const retryInstructionReflectZ = z.object({ reflect: z.string().min(1) }).strict();
|
|
115
|
+
export const nodeRetryZ = z.object({
|
|
116
|
+
max: z.number().int().min(1),
|
|
117
|
+
instruction: z.union([z.string().min(1), retryInstructionAutoZ, retryInstructionReflectZ]).optional(),
|
|
118
|
+
});
|
|
104
119
|
export const nodeZ = z.object({
|
|
105
120
|
name: z.string().min(1),
|
|
106
121
|
instruction: sourceZ,
|
|
@@ -110,6 +125,8 @@ export const nodeZ = z.object({
|
|
|
110
125
|
rules: nodeSourcesZ.optional(),
|
|
111
126
|
context: nodeSourcesZ.optional(),
|
|
112
127
|
verify: nodeVerifyZ.optional(),
|
|
128
|
+
requires: nodeRequiresZ.optional(),
|
|
129
|
+
retry: nodeRetryZ.optional(),
|
|
113
130
|
});
|
|
114
131
|
export const edgeZ = z.object({
|
|
115
132
|
from: z.string().min(1),
|
|
@@ -375,6 +392,86 @@ export const workflowJsonSchema = {
|
|
|
375
392
|
$ref: "#/$defs/NodeSources",
|
|
376
393
|
description: "Per-node context. Additive by default; set { only: true } to block cascade.",
|
|
377
394
|
},
|
|
395
|
+
verify: {
|
|
396
|
+
type: "object",
|
|
397
|
+
description: "Machine-checked post-conditions evaluated after the LLM finishes.",
|
|
398
|
+
additionalProperties: false,
|
|
399
|
+
properties: {
|
|
400
|
+
any_tool_called: { type: "array", items: { type: "string", minLength: 1 }, minItems: 1 },
|
|
401
|
+
all_tools_called: { type: "array", items: { type: "string", minLength: 1 }, minItems: 1 },
|
|
402
|
+
no_tool_called: { type: "array", items: { type: "string", minLength: 1 }, minItems: 1 },
|
|
403
|
+
output_required: { type: "array", items: { type: "string", minLength: 1 }, minItems: 1 },
|
|
404
|
+
output_matches: {
|
|
405
|
+
type: "array",
|
|
406
|
+
items: {
|
|
407
|
+
type: "object",
|
|
408
|
+
required: ["path"],
|
|
409
|
+
additionalProperties: false,
|
|
410
|
+
properties: {
|
|
411
|
+
path: { type: "string", minLength: 1 },
|
|
412
|
+
equals: {},
|
|
413
|
+
in: { type: "array" },
|
|
414
|
+
matches: { type: "string", minLength: 1 },
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
minItems: 1,
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
requires: {
|
|
422
|
+
type: "object",
|
|
423
|
+
description: "Pre-condition checks evaluated before the LLM runs.",
|
|
424
|
+
additionalProperties: false,
|
|
425
|
+
properties: {
|
|
426
|
+
output_required: {
|
|
427
|
+
type: "array",
|
|
428
|
+
items: { type: "string", minLength: 1 },
|
|
429
|
+
minItems: 1,
|
|
430
|
+
},
|
|
431
|
+
output_matches: {
|
|
432
|
+
type: "array",
|
|
433
|
+
items: {
|
|
434
|
+
type: "object",
|
|
435
|
+
required: ["path"],
|
|
436
|
+
additionalProperties: false,
|
|
437
|
+
properties: {
|
|
438
|
+
path: { type: "string", minLength: 1 },
|
|
439
|
+
equals: {},
|
|
440
|
+
in: { type: "array" },
|
|
441
|
+
matches: { type: "string", minLength: 1 },
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
minItems: 1,
|
|
445
|
+
},
|
|
446
|
+
on_fail: { type: "string", enum: ["fail", "skip"] },
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
retry: {
|
|
450
|
+
type: "object",
|
|
451
|
+
description: "Node-local retry on verify failure.",
|
|
452
|
+
required: ["max"],
|
|
453
|
+
additionalProperties: false,
|
|
454
|
+
properties: {
|
|
455
|
+
max: { type: "integer", minimum: 1 },
|
|
456
|
+
instruction: {
|
|
457
|
+
oneOf: [
|
|
458
|
+
{ type: "string", minLength: 1 },
|
|
459
|
+
{
|
|
460
|
+
type: "object",
|
|
461
|
+
required: ["auto"],
|
|
462
|
+
additionalProperties: false,
|
|
463
|
+
properties: { auto: { const: true } },
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
type: "object",
|
|
467
|
+
required: ["reflect"],
|
|
468
|
+
additionalProperties: false,
|
|
469
|
+
properties: { reflect: { type: "string", minLength: 1 } },
|
|
470
|
+
},
|
|
471
|
+
],
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
},
|
|
378
475
|
},
|
|
379
476
|
},
|
|
380
477
|
},
|
package/dist/testing.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ export interface MockClaudeOptions {
|
|
|
38
38
|
routes?: Record<string, string>;
|
|
39
39
|
/** Workflow definition — enables instruction-based node matching (required for branching workflows) */
|
|
40
40
|
workflow?: Workflow;
|
|
41
|
+
/** Scripted handler for `ask()` calls. */
|
|
42
|
+
ask?: (instruction: string, context: Record<string, unknown>) => string;
|
|
41
43
|
}
|
|
42
44
|
/**
|
|
43
45
|
* A mock Claude client that follows a script.
|
|
@@ -51,6 +53,7 @@ export declare class MockClaude implements Claude {
|
|
|
51
53
|
private responses;
|
|
52
54
|
private routes;
|
|
53
55
|
private instructionMap;
|
|
56
|
+
private askFn?;
|
|
54
57
|
constructor(opts: MockClaudeOptions);
|
|
55
58
|
/** Returns the order in which nodes were executed */
|
|
56
59
|
get executedNodes(): string[];
|
|
@@ -68,6 +71,10 @@ export declare class MockClaude implements Claude {
|
|
|
68
71
|
description: string;
|
|
69
72
|
}[];
|
|
70
73
|
}): Promise<string>;
|
|
74
|
+
ask(opts: {
|
|
75
|
+
instruction: string;
|
|
76
|
+
context: Record<string, unknown>;
|
|
77
|
+
}): Promise<string>;
|
|
71
78
|
/**
|
|
72
79
|
* Identify which node is being executed.
|
|
73
80
|
*
|
package/dist/testing.js
CHANGED
|
@@ -32,9 +32,11 @@ export class MockClaude {
|
|
|
32
32
|
responses;
|
|
33
33
|
routes;
|
|
34
34
|
instructionMap; // instruction text → node ID
|
|
35
|
+
askFn;
|
|
35
36
|
constructor(opts) {
|
|
36
37
|
this.responses = opts.responses;
|
|
37
38
|
this.routes = opts.routes ?? {};
|
|
39
|
+
this.askFn = opts.ask;
|
|
38
40
|
// Build reverse map: instruction → node ID (for accurate matching in branching workflows)
|
|
39
41
|
this.instructionMap = new Map();
|
|
40
42
|
if (opts.workflow) {
|
|
@@ -98,6 +100,11 @@ export class MockClaude {
|
|
|
98
100
|
// Default: first choice
|
|
99
101
|
return opts.choices[0].id;
|
|
100
102
|
}
|
|
103
|
+
async ask(opts) {
|
|
104
|
+
if (this.askFn)
|
|
105
|
+
return this.askFn(opts.instruction, opts.context);
|
|
106
|
+
return "";
|
|
107
|
+
}
|
|
101
108
|
/**
|
|
102
109
|
* Identify which node is being executed.
|
|
103
110
|
*
|
package/dist/types.d.ts
CHANGED
|
@@ -73,6 +73,50 @@ export interface NodeVerify {
|
|
|
73
73
|
/** Each assertion must hold against `result.data`. */
|
|
74
74
|
output_matches?: OutputMatch[];
|
|
75
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Machine-checked pre-condition for a node.
|
|
78
|
+
*
|
|
79
|
+
* Evaluated by the executor BEFORE the LLM runs. If any declared check fails,
|
|
80
|
+
* the node is marked `failed` (or `skipped` when `on_fail: "skip"`) and the
|
|
81
|
+
* LLM is never invoked.
|
|
82
|
+
*
|
|
83
|
+
* Path roots resolve against the cross-node context map:
|
|
84
|
+
* { input: <runtime input>, [priorNodeId]: <data of prior node>, ... }
|
|
85
|
+
*
|
|
86
|
+
* Reuses the same path grammar as `verify` (dotted segments, `[*]` wildcard,
|
|
87
|
+
* optional `all:`/`any:` prefix).
|
|
88
|
+
*/
|
|
89
|
+
export interface NodeRequires {
|
|
90
|
+
/** Listed paths must be present and non-null in the context map. */
|
|
91
|
+
output_required?: string[];
|
|
92
|
+
/** Each assertion must hold against the context map. */
|
|
93
|
+
output_matches?: OutputMatch[];
|
|
94
|
+
/** Action when checks fail. Default: "fail". */
|
|
95
|
+
on_fail?: "fail" | "skip";
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Node-local retry on verify failure.
|
|
99
|
+
*
|
|
100
|
+
* Re-runs the LLM up to `max` additional times, prepending feedback derived
|
|
101
|
+
* from the verify failure. Triggered ONLY by verify failure — not by tool/API
|
|
102
|
+
* errors and not by `requires` failure.
|
|
103
|
+
*
|
|
104
|
+
* `instruction` shapes the feedback preamble:
|
|
105
|
+
* - omitted → default "## Previous attempt failed verification..."
|
|
106
|
+
* - string → static text + verify error
|
|
107
|
+
* - { auto: true } → LLM-generated diagnosis from default reflection prompt
|
|
108
|
+
* - { reflect: s } → LLM-generated diagnosis from author-provided prompt
|
|
109
|
+
*/
|
|
110
|
+
export interface NodeRetry {
|
|
111
|
+
/** Maximum number of retry attempts after the initial run. Must be ≥ 1. */
|
|
112
|
+
max: number;
|
|
113
|
+
/** Preamble shape — see interface docs. */
|
|
114
|
+
instruction?: string | {
|
|
115
|
+
auto: true;
|
|
116
|
+
} | {
|
|
117
|
+
reflect: string;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
76
120
|
/**
|
|
77
121
|
* A single output assertion. Exactly one of `equals | in | matches` must be set.
|
|
78
122
|
*
|
|
@@ -106,6 +150,10 @@ export interface Node {
|
|
|
106
150
|
context?: NodeSources;
|
|
107
151
|
/** Machine-checked post-conditions. Enforced by the executor after the LLM finishes. */
|
|
108
152
|
verify?: NodeVerify;
|
|
153
|
+
/** Machine-checked pre-conditions. Enforced by the executor before the LLM runs. */
|
|
154
|
+
requires?: NodeRequires;
|
|
155
|
+
/** Node-local retry on verify failure (with optional autonomous reflection). */
|
|
156
|
+
retry?: NodeRetry;
|
|
109
157
|
}
|
|
110
158
|
/** An edge connecting two nodes */
|
|
111
159
|
export interface Edge {
|
|
@@ -182,6 +230,12 @@ export type ExecutionEvent = {
|
|
|
182
230
|
type: "node:progress";
|
|
183
231
|
node: string;
|
|
184
232
|
message: string;
|
|
233
|
+
} | {
|
|
234
|
+
type: "node:retry";
|
|
235
|
+
node: string;
|
|
236
|
+
attempt: number;
|
|
237
|
+
reason: string;
|
|
238
|
+
preamble: string;
|
|
185
239
|
} | {
|
|
186
240
|
type: "route";
|
|
187
241
|
from: string;
|
|
@@ -200,6 +254,8 @@ export interface TraceStep {
|
|
|
200
254
|
status: "success" | "failed" | "skipped";
|
|
201
255
|
/** 1-based iteration count (2 = second time this node ran) */
|
|
202
256
|
iteration: number;
|
|
257
|
+
/** 0-indexed retry attempt for this iteration. Absent when no retry fired. */
|
|
258
|
+
retryAttempt?: number;
|
|
203
259
|
}
|
|
204
260
|
/** A routing decision between nodes */
|
|
205
261
|
export interface TraceEdge {
|
|
@@ -245,6 +301,14 @@ export interface Claude {
|
|
|
245
301
|
description: string;
|
|
246
302
|
}[];
|
|
247
303
|
}): Promise<string>;
|
|
304
|
+
/**
|
|
305
|
+
* Single-completion free-text query. No tools, no output schema.
|
|
306
|
+
* Used by the executor to generate retry strategies in autonomous reflection mode.
|
|
307
|
+
*/
|
|
308
|
+
ask(opts: {
|
|
309
|
+
instruction: string;
|
|
310
|
+
context: Record<string, unknown>;
|
|
311
|
+
}): Promise<string>;
|
|
248
312
|
}
|
|
249
313
|
export interface McpServerConfig {
|
|
250
314
|
type?: "stdio" | "http";
|
package/dist/verify.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export type Resolution = {
|
|
|
8
8
|
reason: string;
|
|
9
9
|
};
|
|
10
10
|
export declare function resolvePath(data: unknown, path: string): Resolution;
|
|
11
|
+
export declare function isErrorOutput(output: unknown): boolean;
|
|
11
12
|
export declare function checkAnyToolCalled(required: string[], toolCalls: ToolCall[]): string | null;
|
|
12
13
|
export declare function checkAllToolsCalled(required: string[], toolCalls: ToolCall[]): string | null;
|
|
13
14
|
export declare function checkNoToolCalled(forbidden: string[], toolCalls: ToolCall[]): string | null;
|
package/dist/verify.js
CHANGED
|
@@ -72,7 +72,7 @@ export function resolvePath(data, path) {
|
|
|
72
72
|
// A tool call "succeeded" when its output does not carry a non-null `error` field.
|
|
73
73
|
// `{ error: null }` and `{ error: false }` are treated as success — many real
|
|
74
74
|
// tools include the key with a null sentinel.
|
|
75
|
-
function isErrorOutput(output) {
|
|
75
|
+
export function isErrorOutput(output) {
|
|
76
76
|
if (!output || typeof output !== "object")
|
|
77
77
|
return false;
|
|
78
78
|
const err = output.error;
|
|
@@ -73,6 +73,9 @@ describe("buildWorkflow", () => {
|
|
|
73
73
|
async evaluate() {
|
|
74
74
|
return "";
|
|
75
75
|
},
|
|
76
|
+
async ask() {
|
|
77
|
+
return "";
|
|
78
|
+
},
|
|
76
79
|
};
|
|
77
80
|
const opts = { claude: capturingClaude, skills: [mockSkill] };
|
|
78
81
|
await buildWorkflow("Triage Sentry errors", opts);
|
|
@@ -108,6 +111,9 @@ describe("refineWorkflow", () => {
|
|
|
108
111
|
async evaluate() {
|
|
109
112
|
return "";
|
|
110
113
|
},
|
|
114
|
+
async ask() {
|
|
115
|
+
return "";
|
|
116
|
+
},
|
|
111
117
|
};
|
|
112
118
|
const opts = { claude: capturingClaude, skills: [mockSkill] };
|
|
113
119
|
await refineWorkflow(validWorkflow, "Add a notification node", opts);
|