🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@agent-pattern-labs/iso-postflight

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@agent-pattern-labs/iso-postflight - npm Package Compare versions

Comparing version
0.1.1
to
0.1.2
+108
-12
dist/postflight.js

@@ -15,5 +15,7 @@ import { isJsonObject, isJsonValue } from "./json.js";

throw new Error("postflight config workflows must be an array");
const workflows = input.workflows.map((workflow, index) => normalizeWorkflow(workflow, `workflows[${index}]`));
assertUnique(workflows.map((workflow) => workflow.name), "workflow name");
return {
version: 1,
workflows: input.workflows.map((workflow, index) => normalizeWorkflow(workflow, `workflows[${index}]`)),
workflows,
};

@@ -27,5 +29,8 @@ }

throw new Error("postflight plan rounds must be an array");
const rounds = input.rounds.map((round, index) => normalizePlanRound(round, index));
assertUnique(rounds.map((round) => String(round.index)), "round index");
assertUnique(rounds.flatMap((round) => round.candidates.map((candidate) => candidate.id)), "plan candidate id");
return {
workflow,
rounds: input.rounds.map((round, index) => normalizePlanRound(round, index)),
rounds,
postSteps: optionalSteps(input.postSteps, "plan.postSteps") ?? [],

@@ -48,2 +53,3 @@ };

const workflow = selectWorkflow(config, options.workflow || plan.workflow);
validateRelationships(workflow, plan, observations, options);
const outcomeMap = new Map(observations.outcomes.map((outcome) => [outcome.candidateId, outcome]));

@@ -55,3 +61,3 @@ const dispatchSet = new Set([

const rounds = plan.rounds.map((round) => settleRound(workflow, round, outcomeMap, dispatchSet));
const postSteps = settlePostSteps(plan.postSteps.length ? plan.postSteps : workflow.postSteps, observations.steps);
const postSteps = settlePostSteps(mergePostSteps(workflow.postSteps, plan.postSteps), observations.steps);
const issues = [

@@ -87,2 +93,11 @@ ...rounds.flatMap((round) => round.candidates.flatMap((candidate) => candidate.issues)),

const skipStatuses = optionalStringArray(input.skipStatuses, `${label}.skipStatuses`) ?? DEFAULT_SKIP_STATUSES;
const inFlightStatuses = optionalStringArray(input.inFlightStatuses, `${label}.inFlightStatuses`) ?? DEFAULT_IN_FLIGHT_STATUSES;
const replacementStatuses = optionalStringArray(input.replacementStatuses, `${label}.replacementStatuses`) ?? [];
validateStatusCategories(label, {
successStatuses,
failureStatuses,
skipStatuses,
inFlightStatuses,
replacementStatuses,
});
const terminalStatuses = optionalStringArray(input.terminalStatuses, `${label}.terminalStatuses`) ?? [

@@ -100,4 +115,4 @@ ...successStatuses,

skipStatuses,
inFlightStatuses: optionalStringArray(input.inFlightStatuses, `${label}.inFlightStatuses`) ?? DEFAULT_IN_FLIGHT_STATUSES,
replacementStatuses: optionalStringArray(input.replacementStatuses, `${label}.replacementStatuses`) ?? [],
inFlightStatuses,
replacementStatuses,
requiredArtifacts: optionalRequiredArtifacts(input.requiredArtifacts, `${label}.requiredArtifacts`) ?? [],

@@ -138,3 +153,5 @@ postSteps: optionalSteps(input.postSteps, `${label}.postSteps`) ?? [],

throw new Error(`${label} must be an array`);
return input.map((dispatch, index) => normalizeDispatch(dispatch, `${label}[${index}]`));
const dispatches = input.map((dispatch, index) => normalizeDispatch(dispatch, `${label}[${index}]`));
assertUnique(dispatches.map((dispatch) => dispatch.candidateId), "dispatch candidate id");
return dispatches;
}

@@ -155,3 +172,5 @@ function normalizeDispatch(input, label) {

throw new Error(`${label} must be an array`);
return input.map((outcome, index) => normalizeOutcome(outcome, `${label}[${index}]`));
const outcomes = input.map((outcome, index) => normalizeOutcome(outcome, `${label}[${index}]`));
assertUnique(outcomes.map((outcome) => outcome.candidateId), "outcome candidate id");
return outcomes;
}

@@ -172,4 +191,7 @@ function normalizeOutcome(input, label) {

return [];
if (Array.isArray(input))
return input.map((artifact, index) => normalizeArtifact(artifact, `${label}[${index}]`));
if (Array.isArray(input)) {
const artifacts = input.map((artifact, index) => normalizeArtifact(artifact, `${label}[${index}]`));
assertUnique(artifacts.map((artifact) => artifact.id), "artifact id");
return artifacts;
}
if (!isJsonObject(input))

@@ -203,3 +225,5 @@ throw new Error(`${label} must be an array or object`);

throw new Error(`${label} must be an array`);
return input.map((step, index) => normalizeStepObservation(step, `${label}[${index}]`));
const steps = input.map((step, index) => normalizeStepObservation(step, `${label}[${index}]`));
assertUnique(steps.map((step) => step.id), "step observation id");
return steps;
}

@@ -220,3 +244,5 @@ function normalizeStepObservation(input, label) {

throw new Error(`${label} must be an array`);
return input.map((artifact, index) => normalizeRequiredArtifact(artifact, `${label}[${index}]`));
const artifacts = input.map((artifact, index) => normalizeRequiredArtifact(artifact, `${label}[${index}]`));
assertUnique(artifacts.map((artifact) => artifact.id), "required artifact id");
return artifacts;
}

@@ -237,3 +263,5 @@ function normalizeRequiredArtifact(input, label) {

throw new Error(`${label} must be an array`);
return input.map((step, index) => normalizeStep(step, `${label}[${index}]`));
const steps = input.map((step, index) => normalizeStep(step, `${label}[${index}]`));
assertUnique(steps.map((step) => step.id), "post step id");
return steps;
}

@@ -485,2 +513,22 @@ function normalizeStep(input, label) {

}
function validateRelationships(workflow, plan, observations, options) {
if (options.workflow && plan.workflow && options.workflow !== plan.workflow) {
throw new Error(`plan workflow "${plan.workflow}" does not match requested workflow "${options.workflow}"`);
}
const candidateIds = new Set(plan.rounds.flatMap((round) => round.candidates.map((candidate) => candidate.id)));
for (const dispatch of observations.dispatches) {
if (!candidateIds.has(dispatch.candidateId))
throw new Error(`foreign dispatch candidate id "${dispatch.candidateId}" is not in the plan`);
}
for (const outcome of observations.outcomes) {
if (!candidateIds.has(outcome.candidateId))
throw new Error(`foreign outcome candidate id "${outcome.candidateId}" is not in the plan`);
}
const effectiveSteps = mergePostSteps(workflow.postSteps, plan.postSteps);
const effectiveStepIds = new Set(effectiveSteps.map((step) => step.id));
for (const step of observations.steps) {
if (!effectiveStepIds.has(step.id))
throw new Error(`foreign step observation id "${step.id}" is not in the plan`);
}
}
function normalizeMeta(input, label) {

@@ -529,2 +577,50 @@ if (input === undefined)

}
function validateStatusCategories(label, categories) {
const exclusive = [
["successStatuses", categories.successStatuses],
["failureStatuses", categories.failureStatuses],
["skipStatuses", categories.skipStatuses],
["inFlightStatuses", categories.inFlightStatuses],
];
for (let index = 0; index < exclusive.length; index += 1) {
for (let otherIndex = index + 1; otherIndex < exclusive.length; otherIndex += 1) {
const [leftName, left] = exclusive[index];
const [rightName, right] = exclusive[otherIndex];
assertNoStatusOverlap(label, leftName, left, rightName, right);
}
}
// Replacement is a refinement of failure when the two overlap. It must not
// compete with success, skip, or in-flight classification.
assertNoStatusOverlap(label, "replacementStatuses", categories.replacementStatuses, "successStatuses", categories.successStatuses);
assertNoStatusOverlap(label, "replacementStatuses", categories.replacementStatuses, "skipStatuses", categories.skipStatuses);
assertNoStatusOverlap(label, "replacementStatuses", categories.replacementStatuses, "inFlightStatuses", categories.inFlightStatuses);
}
function assertNoStatusOverlap(label, leftName, left, rightName, right) {
const rightStatuses = statusSet(right);
const overlap = left.map(normalizeStatus).find((status) => rightStatuses.has(status));
if (overlap) {
throw new Error(`${label} status "${overlap}" appears in both ${leftName} and ${rightName}`);
}
}
function mergePostSteps(required, planned) {
const order = [];
const byId = new Map();
for (const step of [...required, ...planned]) {
if (!byId.has(step.id))
order.push(step.id);
const previous = byId.get(step.id);
byId.set(step.id, previous
? { ...previous, ...step, command: step.command ?? previous.command }
: step);
}
return order.map((id) => byId.get(id));
}
function assertUnique(values, label) {
const seen = new Set();
for (const value of values) {
if (seen.has(value))
throw new Error(`duplicate ${label} "${value}"`);
seen.add(value);
}
}
//# sourceMappingURL=postflight.js.map
+1
-1
{
"name": "@agent-pattern-labs/iso-postflight",
"version": "0.1.1",
"version": "0.1.2",
"description": "Deterministic postflight settlement for AI-agent workflows: reconcile dispatch plans, observed outcomes, artifacts, and post-steps without model calls.",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -107,2 +107,15 @@ # @agent-pattern-labs/iso-postflight

Candidate, round, outcome, dispatch, artifact, and step IDs must be unique and
candidate observations must refer to IDs declared by the selected plan.
Required artifact IDs are requirements, not an allowlist: outcomes may include
additional artifact evidence. Workflow post-steps always remain required;
plan post-steps can override matching presentation details or add supplemental
steps, but cannot remove workflow requirements. Step observations must refer to
one of those effective workflow or plan steps.
Success, failure, skip, and in-flight status categories are compared after
trimming and case-folding and must not overlap. Replacement statuses may
overlap failure statuses intentionally—replacement is checked first—but cannot
overlap success, skip, or in-flight statuses.
## Outcome shape

@@ -109,0 +122,0 @@