@brainervirus/workit-cursor 1.0.10 → 1.0.12
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/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +377 -218
- package/dist/mcp-server.js +2980 -2870
- package/dist/workit-hook.js +346 -216
- package/package.json +3 -3
package/dist/workit-hook.js
CHANGED
|
@@ -7355,6 +7355,89 @@ var refSchema = discriminatedUnion("kind", [
|
|
|
7355
7355
|
object({ kind: literal("host"), host: hostSchema, handle: nonEmpty }).strict(),
|
|
7356
7356
|
object({ kind: literal("external"), url: text.url() }).strict()
|
|
7357
7357
|
]);
|
|
7358
|
+
var rewriteUnknown = (value, map) => {
|
|
7359
|
+
if (Array.isArray(value)) {
|
|
7360
|
+
const out = [];
|
|
7361
|
+
for (const item of value) {
|
|
7362
|
+
const rewritten = rewriteUnknown(item, map);
|
|
7363
|
+
if (!rewritten.drop)
|
|
7364
|
+
out.push(rewritten.value);
|
|
7365
|
+
}
|
|
7366
|
+
return { drop: false, value: out };
|
|
7367
|
+
}
|
|
7368
|
+
if (typeof value === "object" && value !== null) {
|
|
7369
|
+
if (refSchema.safeParse(value).success) {
|
|
7370
|
+
const mapped = map(value);
|
|
7371
|
+
return mapped === null ? { drop: true } : { drop: false, value: mapped };
|
|
7372
|
+
}
|
|
7373
|
+
const out = {};
|
|
7374
|
+
for (const [key, item] of Object.entries(value)) {
|
|
7375
|
+
const rewritten = rewriteUnknown(item, map);
|
|
7376
|
+
out[key] = rewritten.drop ? null : rewritten.value;
|
|
7377
|
+
}
|
|
7378
|
+
return { drop: false, value: out };
|
|
7379
|
+
}
|
|
7380
|
+
return { drop: false, value };
|
|
7381
|
+
};
|
|
7382
|
+
var rewriteNode = (value, schema, map) => {
|
|
7383
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && refSchema.safeParse(value).success) {
|
|
7384
|
+
const mapped = map(value);
|
|
7385
|
+
return mapped === null ? { drop: true } : { drop: false, value: mapped };
|
|
7386
|
+
}
|
|
7387
|
+
let node = schema;
|
|
7388
|
+
while (node instanceof ZodNullable || node instanceof ZodOptional || node instanceof ZodDefault) {
|
|
7389
|
+
node = node.unwrap();
|
|
7390
|
+
}
|
|
7391
|
+
if (node === refSchema) {
|
|
7392
|
+
if (!refSchema.safeParse(value).success)
|
|
7393
|
+
return { drop: false, value };
|
|
7394
|
+
const mapped = map(value);
|
|
7395
|
+
return mapped === null ? { drop: true } : { drop: false, value: mapped };
|
|
7396
|
+
}
|
|
7397
|
+
if (node instanceof ZodArray) {
|
|
7398
|
+
if (!Array.isArray(value))
|
|
7399
|
+
return { drop: false, value };
|
|
7400
|
+
const out = [];
|
|
7401
|
+
for (const item of value) {
|
|
7402
|
+
const rewritten = rewriteNode(item, node.element, map);
|
|
7403
|
+
if (!rewritten.drop)
|
|
7404
|
+
out.push(rewritten.value);
|
|
7405
|
+
}
|
|
7406
|
+
return { drop: false, value: out };
|
|
7407
|
+
}
|
|
7408
|
+
if (node instanceof ZodObject) {
|
|
7409
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
7410
|
+
return { drop: false, value };
|
|
7411
|
+
const shape = node.shape;
|
|
7412
|
+
const record = value;
|
|
7413
|
+
const out = {};
|
|
7414
|
+
for (const [key, item] of Object.entries(record)) {
|
|
7415
|
+
if (!(key in shape)) {
|
|
7416
|
+
const scrubbed = rewriteUnknown(item, map);
|
|
7417
|
+
out[key] = scrubbed.drop ? null : scrubbed.value;
|
|
7418
|
+
continue;
|
|
7419
|
+
}
|
|
7420
|
+
const child = shape[key];
|
|
7421
|
+
const rewritten = rewriteNode(item, child, map);
|
|
7422
|
+
if (rewritten.drop) {
|
|
7423
|
+
if (child instanceof ZodOptional || child instanceof ZodDefault)
|
|
7424
|
+
continue;
|
|
7425
|
+
if (child instanceof ZodNullable) {
|
|
7426
|
+
out[key] = null;
|
|
7427
|
+
continue;
|
|
7428
|
+
}
|
|
7429
|
+
return { drop: true };
|
|
7430
|
+
}
|
|
7431
|
+
out[key] = rewritten.value;
|
|
7432
|
+
}
|
|
7433
|
+
return { drop: false, value: out };
|
|
7434
|
+
}
|
|
7435
|
+
return { drop: false, value };
|
|
7436
|
+
};
|
|
7437
|
+
var rewriteRecordRefs = (value, schema, map) => {
|
|
7438
|
+
const rewritten = rewriteNode(value, schema, map);
|
|
7439
|
+
return rewritten.drop ? null : rewritten.value;
|
|
7440
|
+
};
|
|
7358
7441
|
var provenanceSchema = object({
|
|
7359
7442
|
kind: _enum(["host_observed", "agent_reported", "imported"]),
|
|
7360
7443
|
host: hostSchema,
|
|
@@ -7574,10 +7657,11 @@ var decisionSchema = object({
|
|
|
7574
7657
|
presented: text,
|
|
7575
7658
|
approvedContent: text,
|
|
7576
7659
|
displayed: text.optional(),
|
|
7577
|
-
contentRefs: array(refSchema)
|
|
7660
|
+
contentRefs: array(refSchema),
|
|
7661
|
+
statedChoice: object({ ref: text, text }).strict().optional()
|
|
7578
7662
|
}).strict(),
|
|
7579
7663
|
digest,
|
|
7580
|
-
response: _enum(["approved", "rejected"]),
|
|
7664
|
+
response: _enum(["approved", "rejected", "stated"]),
|
|
7581
7665
|
requirementIds: array(digest),
|
|
7582
7666
|
revoked: object({ at: utc, reason: text }).strict().nullable(),
|
|
7583
7667
|
consumption: object({
|
|
@@ -9004,11 +9088,11 @@ class TaskStore {
|
|
|
9004
9088
|
return workspace;
|
|
9005
9089
|
if (!workspace.data)
|
|
9006
9090
|
return failure("not_found", "workspace not found");
|
|
9007
|
-
const
|
|
9008
|
-
if (!
|
|
9091
|
+
const expected = input.expectedRevision;
|
|
9092
|
+
if (!expected)
|
|
9009
9093
|
return failure("invalid_input", "task revision is required");
|
|
9010
|
-
if (task.data.revision !==
|
|
9011
|
-
return this.conflict(
|
|
9094
|
+
if (task.data.revision !== expected)
|
|
9095
|
+
return this.conflict(expected, task.data.revision);
|
|
9012
9096
|
if (workspace.data.revision !== input.expectedWorkspaceRevision)
|
|
9013
9097
|
return this.conflict(input.expectedWorkspaceRevision, workspace.data.revision);
|
|
9014
9098
|
const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
|
|
@@ -9542,6 +9626,7 @@ var same = (left, right) => {
|
|
|
9542
9626
|
};
|
|
9543
9627
|
var UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
|
|
9544
9628
|
var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
9629
|
+
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
9545
9630
|
var callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor };
|
|
9546
9631
|
var validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
|
|
9547
9632
|
const code = char.charCodeAt(0);
|
|
@@ -9603,6 +9688,36 @@ var trustedAuthority = (kind, provenance, expected, binding, caller) => {
|
|
|
9603
9688
|
verifiedAuthorities.set(token, { kind, provenance, ...expected, ...binding, caller });
|
|
9604
9689
|
return token;
|
|
9605
9690
|
};
|
|
9691
|
+
var chainStepKey = (step) => step.kind === "commit" ? step.message : step.kind === "branch" ? `branch:${step.target}` : "pr";
|
|
9692
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9693
|
+
var normalizeChainSteps = (steps) => {
|
|
9694
|
+
if (!Array.isArray(steps) || steps.length === 0 || steps.length > 32)
|
|
9695
|
+
return null;
|
|
9696
|
+
const normalized = [];
|
|
9697
|
+
for (const step of steps) {
|
|
9698
|
+
if (typeof step === "string") {
|
|
9699
|
+
if (!step.trim())
|
|
9700
|
+
return null;
|
|
9701
|
+
normalized.push({ kind: "commit", message: step });
|
|
9702
|
+
continue;
|
|
9703
|
+
}
|
|
9704
|
+
if (!isRecord(step))
|
|
9705
|
+
return null;
|
|
9706
|
+
if (typeof step.branch === "string" && step.branch.trim() && Object.keys(step).length === 1) {
|
|
9707
|
+
normalized.push({ kind: "branch", target: step.branch });
|
|
9708
|
+
continue;
|
|
9709
|
+
}
|
|
9710
|
+
if (step.pr === true && Object.keys(step).length === 1) {
|
|
9711
|
+
normalized.push({ kind: "pr" });
|
|
9712
|
+
continue;
|
|
9713
|
+
}
|
|
9714
|
+
return null;
|
|
9715
|
+
}
|
|
9716
|
+
const keys = normalized.map(chainStepKey);
|
|
9717
|
+
if (new Set(keys).size !== keys.length)
|
|
9718
|
+
return null;
|
|
9719
|
+
return normalized;
|
|
9720
|
+
};
|
|
9606
9721
|
var scopeEqual = (left, right) => canonicalJson(left) === canonicalJson(right);
|
|
9607
9722
|
var parseSteps = (content) => {
|
|
9608
9723
|
let value;
|
|
@@ -9614,7 +9729,10 @@ var parseSteps = (content) => {
|
|
|
9614
9729
|
if (typeof value !== "object" || value === null || !("steps" in value))
|
|
9615
9730
|
return [];
|
|
9616
9731
|
const steps = value.steps;
|
|
9617
|
-
|
|
9732
|
+
if (!Array.isArray(steps))
|
|
9733
|
+
return null;
|
|
9734
|
+
const normalized = normalizeChainSteps(steps);
|
|
9735
|
+
return normalized;
|
|
9618
9736
|
};
|
|
9619
9737
|
var decisionMatches = (entry, purpose, binding) => {
|
|
9620
9738
|
const decision = entry.data;
|
|
@@ -9805,14 +9923,12 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
9805
9923
|
return content;
|
|
9806
9924
|
const requestedSteps = parseSteps(decision.binding.approvedContent);
|
|
9807
9925
|
if (requestedSteps === null)
|
|
9808
|
-
return failure("invalid_input", "bounded action steps must be a
|
|
9809
|
-
if (input.steps && canonicalJson(input.steps) !== canonicalJson(requestedSteps))
|
|
9926
|
+
return failure("invalid_input", "bounded action steps must be a unique non-empty array");
|
|
9927
|
+
if (input.steps && canonicalJson(input.steps) !== canonicalJson(requestedSteps.map(chainStepKey)))
|
|
9810
9928
|
return failure("permission_denied", "bounded action steps do not match approved content");
|
|
9811
|
-
if (requestedSteps.some((step) => !step || typeof step !== "string") || new Set(requestedSteps).size !== requestedSteps.length)
|
|
9812
|
-
return failure("invalid_input", "bounded action steps must be unique and non-empty");
|
|
9813
9929
|
const stored = task.actionProgress?.find((item) => item.decisionId === input.decisionId);
|
|
9814
|
-
const workflow = stored ? { steps: stored.steps, completed: stored.completedSteps } : { steps: requestedSteps, completed: [] };
|
|
9815
|
-
if (workflow.steps.length && requestedSteps.length && canonicalJson(workflow.steps) !== canonicalJson(requestedSteps))
|
|
9930
|
+
const workflow = stored ? { steps: stored.steps, completed: stored.completedSteps } : { steps: requestedSteps.map(chainStepKey), completed: [] };
|
|
9931
|
+
if (workflow.steps.length && requestedSteps.length && canonicalJson(workflow.steps) !== canonicalJson(requestedSteps.map(chainStepKey)))
|
|
9816
9932
|
return failure("permission_denied", "bounded action scope changed");
|
|
9817
9933
|
return success(null, null, { entry, workflow });
|
|
9818
9934
|
};
|
|
@@ -10367,13 +10483,28 @@ function evaluateEvidence(task, candidate) {
|
|
|
10367
10483
|
});
|
|
10368
10484
|
}
|
|
10369
10485
|
var scopeCovers2 = scopeCovers;
|
|
10486
|
+
var checkPin = (assignment, beforeCandidateId, candidateId) => {
|
|
10487
|
+
const pin = assignment.candidateId ?? null;
|
|
10488
|
+
if (pin === null)
|
|
10489
|
+
return true;
|
|
10490
|
+
return (beforeCandidateId ?? null) === pin || (candidateId ?? null) === pin;
|
|
10491
|
+
};
|
|
10492
|
+
var resolveBinding = (evidence, pin, currentCandidateId) => {
|
|
10493
|
+
const bound = evidence.kind === "check" || evidence.kind === "review";
|
|
10494
|
+
const fallback = bound ? pin ?? currentCandidateId : null;
|
|
10495
|
+
return {
|
|
10496
|
+
beforeCandidateId: evidence.beforeCandidateId ?? fallback,
|
|
10497
|
+
candidateId: evidence.candidateId ?? fallback
|
|
10498
|
+
};
|
|
10499
|
+
};
|
|
10500
|
+
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId === findingCandidateId);
|
|
10370
10501
|
var applicableDecision2 = (task, workspace, requirement, checkoutRoot) => task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data).filter((decision) => decision.purpose === "limitation" && decision.response === "approved" && decision.revoked === null && decision.binding.taskId === task.id && decision.binding.workspaceId === workspace.id && decision.requirementIds.includes(requirement.id) && decision.binding.scope && scopeCovers2(decision.binding.scope, requirement.scope) && decision.digest === decisionDigest(decision) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, decision.binding).ok : decision.binding.contentRefs.every((reference) => reference.kind !== "file")));
|
|
10371
|
-
var applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && data.response === "approved" && data.revoked === null && data.binding.taskId === task.id && data.binding.workspaceId === workspace.id && data.requirementIds.includes(requirement.id) && scopeCovers2(data.binding.scope, requirement.scope) && data.digest === decisionDigest(data) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok : data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map(({ id }) => ({ id }));
|
|
10372
|
-
var evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
|
|
10502
|
+
var applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && (data.response === "approved" || data.response === "stated") && data.revoked === null && data.binding.taskId === task.id && data.binding.workspaceId === workspace.id && data.requirementIds.includes(requirement.id) && scopeCovers2(data.binding.scope, requirement.scope) && data.digest === decisionDigest(data) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok : data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map(({ id }) => ({ id }));
|
|
10503
|
+
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
10373
10504
|
if (kind !== "passed")
|
|
10374
10505
|
return false;
|
|
10375
10506
|
if (dimension === "testing" || dimension === "verification")
|
|
10376
|
-
return evidenceKind === "check";
|
|
10507
|
+
return evidenceKind === "check" || ruleId === "pre-pr-cleanup" && evidenceKind === "artifact";
|
|
10377
10508
|
if (dimension === "review")
|
|
10378
10509
|
return evidenceKind === "review";
|
|
10379
10510
|
if (dimension === "investigation" || dimension === "challenge")
|
|
@@ -10382,6 +10513,21 @@ var evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
|
|
|
10382
10513
|
return evidenceKind === "artifact";
|
|
10383
10514
|
return false;
|
|
10384
10515
|
};
|
|
10516
|
+
var expectedKinds = (dimension, ruleId) => {
|
|
10517
|
+
if (dimension === "testing")
|
|
10518
|
+
return "check";
|
|
10519
|
+
if (dimension === "verification")
|
|
10520
|
+
return ruleId === "pre-pr-cleanup" ? "check or artifact" : "check";
|
|
10521
|
+
if (dimension === "review")
|
|
10522
|
+
return "review";
|
|
10523
|
+
if (dimension === "investigation" || dimension === "challenge")
|
|
10524
|
+
return "investigation";
|
|
10525
|
+
if (dimension === "artifacts" || dimension === "continuity")
|
|
10526
|
+
return "artifact";
|
|
10527
|
+
if (dimension === "decisions")
|
|
10528
|
+
return "decision";
|
|
10529
|
+
return "worker report";
|
|
10530
|
+
};
|
|
10385
10531
|
function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller) {
|
|
10386
10532
|
const evidence = evaluateEvidence(task, candidate);
|
|
10387
10533
|
if (task.policy && task.policy.policyVersion !== POLICY_VERSION)
|
|
@@ -10395,7 +10541,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10395
10541
|
return (task.policy?.requirements ?? []).map((requirement) => {
|
|
10396
10542
|
const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
|
|
10397
10543
|
const passed = related.filter(({ entry, evaluation }) => {
|
|
10398
|
-
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension))
|
|
10544
|
+
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
|
|
10399
10545
|
return false;
|
|
10400
10546
|
if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
|
|
10401
10547
|
return false;
|
|
@@ -10403,11 +10549,12 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10403
10549
|
return true;
|
|
10404
10550
|
const reviewSession = sessionFromRef(entry.data.reviewContext);
|
|
10405
10551
|
const sameImplementation = sameSession(reviewSession, task.intent.provenance.session);
|
|
10406
|
-
const sameEvidenceSession =
|
|
10552
|
+
const sameEvidenceSession = related.some(({ entry: other }) => other.id !== entry.id && other.data.kind === "review" && other.data.candidateId !== null && other.data.candidateId === entry.data.candidateId && sameSession(reviewSession, other.provenance.session));
|
|
10407
10553
|
return entry.data.kind === "review" && reviewSession !== null && sameSession(reviewSession, entry.provenance.session) && (requirement.ruleId === "self-review" || !sameImplementation && !sameEvidenceSession);
|
|
10408
10554
|
});
|
|
10409
10555
|
if (passed.length) {
|
|
10410
|
-
const
|
|
10556
|
+
const currentId = candidate?.id ?? null;
|
|
10557
|
+
const withRed = requirement.dimension === "testing" ? passed.filter(({ entry }) => related.some((other) => other.entry.data.kind === "check" && other.evaluation.status === "failed" && other.entry.data.requirementIds.includes(requirement.id) && other.entry.recordedAt <= entry.recordedAt) || related.some((baseline) => baseline.entry.id !== entry.id && baseline.entry.data.kind === "check" && baseline.entry.data.result === "passed" && baseline.entry.data.candidateId !== null && currentId !== null && baseline.entry.data.candidateId !== currentId)) : passed;
|
|
10411
10558
|
if (withRed.length)
|
|
10412
10559
|
return {
|
|
10413
10560
|
requirementId: requirement.id,
|
|
@@ -10444,7 +10591,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10444
10591
|
status: "satisfied",
|
|
10445
10592
|
evidenceIds: [],
|
|
10446
10593
|
decisionIds: decisions.map((decision) => decision.id),
|
|
10447
|
-
reason: "an applicable approved decision satisfies the requirement"
|
|
10594
|
+
reason: "an applicable approved or stated decision satisfies the requirement"
|
|
10448
10595
|
};
|
|
10449
10596
|
const limitations = requirement.acceptanceAllowed ? applicableDecision2(task, workspace, requirement, checkoutRoot) : [];
|
|
10450
10597
|
if (limitations.length)
|
|
@@ -10456,6 +10603,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10456
10603
|
reason: "an applicable approved limitation permits the missing evidence"
|
|
10457
10604
|
};
|
|
10458
10605
|
const unavailable = capabilities.some((capability) => capability.assurance === "unavailable" && (capability.name === requirement.dimension || capability.surface === requirement.dimension));
|
|
10606
|
+
if (!unavailable && related.length > 0 && !passed.length)
|
|
10607
|
+
return {
|
|
10608
|
+
requirementId: requirement.id,
|
|
10609
|
+
status: "unsatisfied",
|
|
10610
|
+
evidenceIds: related.map(({ entry }) => entry.id),
|
|
10611
|
+
decisionIds: [],
|
|
10612
|
+
reason: `no passing evidence (needs kind:${expectedKinds(requirement.dimension, requirement.ruleId)})`
|
|
10613
|
+
};
|
|
10459
10614
|
return {
|
|
10460
10615
|
requirementId: requirement.id,
|
|
10461
10616
|
status: unavailable ? "unavailable" : "unsatisfied",
|
|
@@ -10480,7 +10635,13 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10480
10635
|
if (entry.data.disposition === "fixed") {
|
|
10481
10636
|
const verified = entry.data.resolution?.evidenceIds.some((id) => {
|
|
10482
10637
|
const evidence = view.task.evidence.find((item) => item.id === id);
|
|
10483
|
-
|
|
10638
|
+
if (!evidence)
|
|
10639
|
+
return false;
|
|
10640
|
+
return findingVerificationPasses(entry.data.candidateId, {
|
|
10641
|
+
kind: evidence.data.kind,
|
|
10642
|
+
candidateId: evidence.data.candidateId,
|
|
10643
|
+
status: evidenceById.get(id) ?? "missing"
|
|
10644
|
+
});
|
|
10484
10645
|
});
|
|
10485
10646
|
if (!verified)
|
|
10486
10647
|
return failure("requirements_unsatisfied", "fixed findings require current verification");
|
|
@@ -10496,7 +10657,12 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10496
10657
|
}
|
|
10497
10658
|
}
|
|
10498
10659
|
}
|
|
10499
|
-
const blocking = evaluations.filter((item) =>
|
|
10660
|
+
const blocking = evaluations.filter((item) => {
|
|
10661
|
+
if (item.status !== "unsatisfied" && item.status !== "unavailable")
|
|
10662
|
+
return false;
|
|
10663
|
+
const requirement = view.task.policy?.requirements.find((candidate) => candidate.id === item.requirementId);
|
|
10664
|
+
return !requirement || requirement.before === "close";
|
|
10665
|
+
});
|
|
10500
10666
|
if (requestedOutcome !== "stopped" && blocking.length)
|
|
10501
10667
|
return failure("requirements_unsatisfied", "applicable requirements are unsatisfied", {
|
|
10502
10668
|
requirementIds: blocking.map((item) => item.requirementId),
|
|
@@ -10539,15 +10705,15 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10539
10705
|
// packages/workit-core/src/core/methods.ts
|
|
10540
10706
|
var METHODS = {
|
|
10541
10707
|
"workit-challenge": { dimensions: ["challenge", "decisions"] },
|
|
10542
|
-
"workit-behavioral-tdd": { dimensions: ["testing"] },
|
|
10543
|
-
"workit-review": { dimensions: ["review"], ruleIds: ["fresh-context-review"] },
|
|
10708
|
+
"workit-behavioral-tdd": { dimensions: ["testing"], ruleIds: ["mechanical-existing-checks"] },
|
|
10709
|
+
"workit-review": { dimensions: ["review"], ruleIds: ["fresh-context-review", "self-review"] },
|
|
10544
10710
|
"workit-plan": { dimensions: ["artifacts", "continuity"] },
|
|
10545
10711
|
"workit-implement": { dimensions: ["delegation"] },
|
|
10546
|
-
"workit-debug": {
|
|
10547
|
-
"workit-handoff": {
|
|
10712
|
+
"workit-debug": {},
|
|
10713
|
+
"workit-handoff": {},
|
|
10548
10714
|
"workit-deslop": { ruleIds: ["pre-pr-cleanup"] }
|
|
10549
10715
|
};
|
|
10550
|
-
var methodMatches = (
|
|
10716
|
+
var methodMatches = (definition, requirement) => definition.dimensions?.includes(requirement.dimension) === true || definition.ruleIds?.includes(requirement.ruleId) === true;
|
|
10551
10717
|
var assuranceFor = (id, definition, capabilities) => {
|
|
10552
10718
|
const keys = new Set([
|
|
10553
10719
|
id,
|
|
@@ -10568,7 +10734,7 @@ var assuranceFor = (id, definition, capabilities) => {
|
|
|
10568
10734
|
};
|
|
10569
10735
|
function selectMethods(policy, capabilities) {
|
|
10570
10736
|
return Object.entries(METHODS).flatMap(([id, definition]) => {
|
|
10571
|
-
const requirements = policy.requirements.filter((requirement) => methodMatches(
|
|
10737
|
+
const requirements = policy.requirements.filter((requirement) => methodMatches(definition, requirement));
|
|
10572
10738
|
if (requirements.length === 0)
|
|
10573
10739
|
return [];
|
|
10574
10740
|
const capability = assuranceFor(id, definition, capabilities);
|
|
@@ -10736,6 +10902,27 @@ var exportDigest = (bundle) => sha256({
|
|
|
10736
10902
|
sourceWorkspaceId: bundle.sourceWorkspaceId,
|
|
10737
10903
|
task: bundle.task
|
|
10738
10904
|
});
|
|
10905
|
+
// packages/workit-core/src/core/config.ts
|
|
10906
|
+
var PRESETS = {
|
|
10907
|
+
gitflow: {
|
|
10908
|
+
allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
|
|
10909
|
+
protected: ["main", "develop", "master", "prod", "production"]
|
|
10910
|
+
},
|
|
10911
|
+
"github-flow": { allowed: ["*"], protected: ["main"] },
|
|
10912
|
+
"trunk-based": { allowed: ["*"], protected: ["main"] },
|
|
10913
|
+
custom: { allowed: [], protected: [] }
|
|
10914
|
+
};
|
|
10915
|
+
var DEFAULTS = {
|
|
10916
|
+
locale: "en",
|
|
10917
|
+
localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
|
|
10918
|
+
timezone: "America/Santiago",
|
|
10919
|
+
branchPolicy: {
|
|
10920
|
+
preset: "gitflow",
|
|
10921
|
+
allowed: [...PRESETS.gitflow.allowed],
|
|
10922
|
+
protected: [...PRESETS.gitflow.protected]
|
|
10923
|
+
},
|
|
10924
|
+
commitPolicy: { preset: "conventional" }
|
|
10925
|
+
};
|
|
10739
10926
|
// packages/workit-core/src/core/external-action.ts
|
|
10740
10927
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
10741
10928
|
object({
|
|
@@ -11273,6 +11460,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11273
11460
|
if (task.data.status === "paused" && (input.state === "running" || input.state === "cancelling"))
|
|
11274
11461
|
return failure("invalid_transition", "paused task cannot run a worker");
|
|
11275
11462
|
const notStarted = input.dispatch === true;
|
|
11463
|
+
if (notStarted && entry.data.state === "stopped" && input.session === null)
|
|
11464
|
+
return success(task.data.revision, workspace.data.revision, entry);
|
|
11276
11465
|
if (notStarted && (input.state !== "stopped" || input.session !== null))
|
|
11277
11466
|
return failure("invalid_input", "a never-dispatched worker stops with no session");
|
|
11278
11467
|
if (notStarted && entry.data.session !== null)
|
|
@@ -11304,7 +11493,7 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11304
11493
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
11305
11494
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
11306
11495
|
taskId: task.data.id,
|
|
11307
|
-
|
|
11496
|
+
expectedRevision: input.expectedRevision,
|
|
11308
11497
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
11309
11498
|
now: input.now,
|
|
11310
11499
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
@@ -11352,90 +11541,22 @@ var importedProvenance = (context) => ({
|
|
|
11352
11541
|
receipts: []
|
|
11353
11542
|
});
|
|
11354
11543
|
var portableTask = (task) => {
|
|
11355
|
-
const
|
|
11356
|
-
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
|
|
11365
|
-
|
|
11366
|
-
|
|
11367
|
-
|
|
11368
|
-
];
|
|
11369
|
-
for (const entry of entries)
|
|
11370
|
-
entry.provenance = portableProvenance(entry.provenance);
|
|
11371
|
-
for (const worker of clone.workers)
|
|
11372
|
-
worker.data.session = null;
|
|
11373
|
-
clone.intent = {
|
|
11374
|
-
...clone.intent,
|
|
11375
|
-
data: { ...clone.intent.data, authorityRefs: portableRefs(clone.intent.data.authorityRefs) }
|
|
11544
|
+
const stripped = rewriteRecordRefs(task, taskRecordSchema, portableRef);
|
|
11545
|
+
if (typeof stripped !== "object" || stripped === null || Array.isArray(stripped))
|
|
11546
|
+
throw new TypeError("portable task rewrite produced a non-record");
|
|
11547
|
+
const clone = structuredClone(stripped);
|
|
11548
|
+
const demote = (facts, signals, consequences) => {
|
|
11549
|
+
for (const fact of facts)
|
|
11550
|
+
Object.assign(fact, portableFact(fact));
|
|
11551
|
+
for (const name of Object.keys(signals)) {
|
|
11552
|
+
const signal = signals[name];
|
|
11553
|
+
Object.assign(signal, portableSignal(signal));
|
|
11554
|
+
}
|
|
11555
|
+
for (const consequence of consequences)
|
|
11556
|
+
Object.assign(consequence.fact, portableFact(consequence.fact));
|
|
11376
11557
|
};
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
return source ? [
|
|
11380
|
-
{
|
|
11381
|
-
...constraint,
|
|
11382
|
-
source,
|
|
11383
|
-
requires: constraint.requires.map((requirement) => ({
|
|
11384
|
-
...requirement,
|
|
11385
|
-
refs: portableRefs(requirement.refs)
|
|
11386
|
-
}))
|
|
11387
|
-
}
|
|
11388
|
-
] : [];
|
|
11389
|
-
});
|
|
11390
|
-
clone.assessments = clone.assessments.map((entry) => ({
|
|
11391
|
-
...entry,
|
|
11392
|
-
data: {
|
|
11393
|
-
...entry.data,
|
|
11394
|
-
facts: entry.data.facts.map(portableFact),
|
|
11395
|
-
signals: Object.fromEntries(Object.entries(entry.data.signals).map(([name, signal]) => [name, portableSignal(signal)])),
|
|
11396
|
-
consequences: entry.data.consequences.map((consequence) => ({
|
|
11397
|
-
...consequence,
|
|
11398
|
-
fact: portableFact(consequence.fact)
|
|
11399
|
-
})),
|
|
11400
|
-
verification: entry.data.verification.map((verification) => ({
|
|
11401
|
-
...verification,
|
|
11402
|
-
availableChecks: portableRefs(verification.availableChecks)
|
|
11403
|
-
}))
|
|
11404
|
-
}
|
|
11405
|
-
}));
|
|
11406
|
-
clone.progress = {
|
|
11407
|
-
...clone.progress,
|
|
11408
|
-
blockers: clone.progress.blockers.map((blocker) => ({
|
|
11409
|
-
...blocker,
|
|
11410
|
-
refs: portableRefs(blocker.refs)
|
|
11411
|
-
}))
|
|
11412
|
-
};
|
|
11413
|
-
clone.evidence = clone.evidence.map((entry) => ({
|
|
11414
|
-
...entry,
|
|
11415
|
-
data: {
|
|
11416
|
-
...entry.data,
|
|
11417
|
-
refs: portableRefs(entry.data.refs),
|
|
11418
|
-
reviewContext: portableRef(entry.data.reviewContext)
|
|
11419
|
-
}
|
|
11420
|
-
}));
|
|
11421
|
-
clone.decisions = clone.decisions.map((entry) => ({
|
|
11422
|
-
...entry,
|
|
11423
|
-
data: {
|
|
11424
|
-
...entry.data,
|
|
11425
|
-
consumption: entry.data.consumption ? portableRef(entry.data.consumption.actionRef) ? {
|
|
11426
|
-
...entry.data.consumption,
|
|
11427
|
-
actionRef: portableRef(entry.data.consumption.actionRef)
|
|
11428
|
-
} : null : null,
|
|
11429
|
-
binding: {
|
|
11430
|
-
...entry.data.binding,
|
|
11431
|
-
contentRefs: portableRefs(entry.data.binding.contentRefs)
|
|
11432
|
-
}
|
|
11433
|
-
}
|
|
11434
|
-
}));
|
|
11435
|
-
clone.findings = clone.findings.map((entry) => ({
|
|
11436
|
-
...entry,
|
|
11437
|
-
data: { ...entry.data, refs: portableRefs(entry.data.refs) }
|
|
11438
|
-
}));
|
|
11558
|
+
for (const entry of clone.assessments)
|
|
11559
|
+
demote(entry.data.facts, entry.data.signals, entry.data.consequences);
|
|
11439
11560
|
clone.candidates = [];
|
|
11440
11561
|
return clone;
|
|
11441
11562
|
};
|
|
@@ -11460,15 +11581,13 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11460
11581
|
allocate(entry.id);
|
|
11461
11582
|
const fresh = (id) => allocate(id);
|
|
11462
11583
|
const provenance = importedProvenance(context);
|
|
11584
|
+
const remap = (ref) => mapRef(ref, ids);
|
|
11463
11585
|
const intent = {
|
|
11464
11586
|
...source.intent,
|
|
11465
11587
|
id: fresh(source.intent.id),
|
|
11466
11588
|
recordedAt: timestamp,
|
|
11467
11589
|
provenance,
|
|
11468
|
-
data:
|
|
11469
|
-
...source.intent.data,
|
|
11470
|
-
authorityRefs: source.intent.data.authorityRefs.map((ref) => mapRef(ref, ids))
|
|
11471
|
-
}
|
|
11590
|
+
data: rewriteRecordRefs(source.intent.data, intentSchema, remap)
|
|
11472
11591
|
};
|
|
11473
11592
|
const assessments = source.assessments.map((entry) => {
|
|
11474
11593
|
const data = entry.data;
|
|
@@ -11477,28 +11596,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11477
11596
|
id: fresh(entry.id),
|
|
11478
11597
|
recordedAt: timestamp,
|
|
11479
11598
|
provenance,
|
|
11480
|
-
data:
|
|
11481
|
-
...data,
|
|
11482
|
-
facts: data.facts.map((fact) => ({
|
|
11483
|
-
...fact,
|
|
11484
|
-
refs: fact.refs.map((ref) => mapRef(ref, ids))
|
|
11485
|
-
})),
|
|
11486
|
-
signals: Object.fromEntries(Object.entries(data.signals).map(([name, signal]) => [
|
|
11487
|
-
name,
|
|
11488
|
-
{ ...signal, refs: signal.refs.map((ref) => mapRef(ref, ids)) }
|
|
11489
|
-
])),
|
|
11490
|
-
consequences: data.consequences.map((consequence) => ({
|
|
11491
|
-
...consequence,
|
|
11492
|
-
fact: {
|
|
11493
|
-
...consequence.fact,
|
|
11494
|
-
refs: consequence.fact.refs.map((ref) => mapRef(ref, ids))
|
|
11495
|
-
}
|
|
11496
|
-
})),
|
|
11497
|
-
verification: data.verification.map((verification) => ({
|
|
11498
|
-
...verification,
|
|
11499
|
-
availableChecks: verification.availableChecks.map((ref) => mapRef(ref, ids))
|
|
11500
|
-
}))
|
|
11501
|
-
}
|
|
11599
|
+
data: rewriteRecordRefs(data, assessmentSchema, remap)
|
|
11502
11600
|
};
|
|
11503
11601
|
});
|
|
11504
11602
|
const evidence = source.evidence.map((entry) => ({
|
|
@@ -11506,11 +11604,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11506
11604
|
id: fresh(entry.id),
|
|
11507
11605
|
recordedAt: timestamp,
|
|
11508
11606
|
provenance,
|
|
11509
|
-
data:
|
|
11510
|
-
...entry.data,
|
|
11511
|
-
refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
|
|
11512
|
-
reviewContext: entry.data.reviewContext ? mapRef(entry.data.reviewContext, ids) : null
|
|
11513
|
-
}
|
|
11607
|
+
data: rewriteRecordRefs(entry.data, evidenceSchema, remap)
|
|
11514
11608
|
}));
|
|
11515
11609
|
const decisions = source.decisions.map((entry) => ({
|
|
11516
11610
|
...entry,
|
|
@@ -11518,21 +11612,23 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11518
11612
|
recordedAt: timestamp,
|
|
11519
11613
|
provenance
|
|
11520
11614
|
}));
|
|
11521
|
-
const findings = source.findings.map((entry) =>
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11615
|
+
const findings = source.findings.map((entry) => {
|
|
11616
|
+
const data = rewriteRecordRefs(entry.data, findingSchema, remap);
|
|
11617
|
+
return {
|
|
11618
|
+
...entry,
|
|
11619
|
+
id: fresh(entry.id),
|
|
11620
|
+
recordedAt: timestamp,
|
|
11621
|
+
provenance,
|
|
11622
|
+
data: {
|
|
11623
|
+
...data,
|
|
11624
|
+
resolution: data.resolution ? {
|
|
11625
|
+
...data.resolution,
|
|
11626
|
+
evidenceIds: data.resolution.evidenceIds.map((id) => ids.get(id) ?? id),
|
|
11627
|
+
decisionIds: data.resolution.decisionIds.map((id) => ids.get(id) ?? id)
|
|
11628
|
+
} : null
|
|
11629
|
+
}
|
|
11630
|
+
};
|
|
11631
|
+
});
|
|
11536
11632
|
const workers = source.workers.map((entry) => ({
|
|
11537
11633
|
...entry,
|
|
11538
11634
|
id: fresh(entry.id),
|
|
@@ -11575,19 +11671,21 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11575
11671
|
closure: null,
|
|
11576
11672
|
assessments,
|
|
11577
11673
|
evidence,
|
|
11578
|
-
decisions: decisions.map((entry) =>
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
...entry
|
|
11582
|
-
|
|
11583
|
-
...
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
|
|
11589
|
-
|
|
11590
|
-
|
|
11674
|
+
decisions: decisions.map((entry) => {
|
|
11675
|
+
const data = rewriteRecordRefs(entry.data, decisionSchema, remap);
|
|
11676
|
+
return {
|
|
11677
|
+
...entry,
|
|
11678
|
+
data: {
|
|
11679
|
+
...data,
|
|
11680
|
+
binding: {
|
|
11681
|
+
...data.binding,
|
|
11682
|
+
taskId: "",
|
|
11683
|
+
workspaceId: destinationWorkspaceId
|
|
11684
|
+
},
|
|
11685
|
+
consumption: null
|
|
11686
|
+
}
|
|
11687
|
+
};
|
|
11688
|
+
}),
|
|
11591
11689
|
...actionProgress ? { actionProgress } : {},
|
|
11592
11690
|
findings,
|
|
11593
11691
|
workers,
|
|
@@ -11670,7 +11768,7 @@ class WorkitCore {
|
|
|
11670
11768
|
activeWorkerBlocker(task, workspace) {
|
|
11671
11769
|
if (workspace.writer)
|
|
11672
11770
|
return failure("recovery_required", "writer ownership must be released first");
|
|
11673
|
-
if (task.workers.some((entry) =>
|
|
11771
|
+
if (task.workers.some((entry) => workerBlocksTransition("close", entry.data.state)))
|
|
11674
11772
|
return failure("recovery_required", "worker state requires reconciliation");
|
|
11675
11773
|
return null;
|
|
11676
11774
|
}
|
|
@@ -11916,37 +12014,33 @@ class WorkitCore {
|
|
|
11916
12014
|
const currentCandidate = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
11917
12015
|
if (!currentCandidate.ok)
|
|
11918
12016
|
return currentCandidate;
|
|
11919
|
-
|
|
11920
|
-
|
|
11921
|
-
evidence.candidateId ??= currentCandidate.data.id;
|
|
11922
|
-
} else {
|
|
11923
|
-
evidence.beforeCandidateId ??= null;
|
|
11924
|
-
evidence.candidateId ??= null;
|
|
11925
|
-
}
|
|
12017
|
+
const pin = helper && helper.ok ? helper.data.data.assignment.candidateId : null;
|
|
12018
|
+
const bound = resolveBinding(evidence, pin, currentCandidate.data.id);
|
|
11926
12019
|
if (helper?.ok) {
|
|
11927
12020
|
const assignment = helper.data.data.assignment;
|
|
11928
|
-
if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || assignment
|
|
12021
|
+
if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || !checkPin(assignment, bound.beforeCandidateId, bound.candidateId) || !refsWithinScope(evidence.refs, assignment.scope) || !refsWithinScope(evidence.refs, task.data.intent.data.scope))
|
|
11929
12022
|
return failure("permission_denied", "evidence is outside the worker assignment");
|
|
11930
12023
|
}
|
|
11931
12024
|
const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
|
|
11932
|
-
for (const candidateId of [
|
|
12025
|
+
for (const candidateId of [bound.beforeCandidateId, bound.candidateId]) {
|
|
11933
12026
|
if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
|
|
11934
12027
|
return failure("invalid_input", "evidence candidate is not a captured candidate");
|
|
11935
12028
|
}
|
|
12029
|
+
const boundEvidence = { ...evidence, ...bound };
|
|
11936
12030
|
const changed = this.store.mutateTask(task.data.id, input.expectedRevision, (current, mutation) => {
|
|
11937
12031
|
const candidates = current.candidates.some((item) => item.id === currentCandidate.data.id) ? current.candidates : [...current.candidates, currentCandidate.data];
|
|
11938
12032
|
const entry = {
|
|
11939
12033
|
id: newId(),
|
|
11940
12034
|
recordedAt: mutation.now,
|
|
11941
12035
|
provenance: provenance(this.context, "agent_reported"),
|
|
11942
|
-
data:
|
|
12036
|
+
data: boundEvidence
|
|
11943
12037
|
};
|
|
11944
12038
|
return success(mutation.revision, null, {
|
|
11945
12039
|
...current,
|
|
11946
12040
|
candidates,
|
|
11947
12041
|
evidence: [...current.evidence, entry],
|
|
11948
12042
|
findings: current.findings.map((finding) => {
|
|
11949
|
-
if (finding.data.disposition === "open" ||
|
|
12043
|
+
if (finding.data.disposition === "open" || boundEvidence.result === "skipped")
|
|
11950
12044
|
return finding;
|
|
11951
12045
|
if (finding.data.disposition !== "fixed")
|
|
11952
12046
|
return finding;
|
|
@@ -11955,8 +12049,12 @@ class WorkitCore {
|
|
|
11955
12049
|
item.evidenceId,
|
|
11956
12050
|
item.status
|
|
11957
12051
|
]));
|
|
11958
|
-
const verified = withEntry.evidence.some((item) =>
|
|
11959
|
-
|
|
12052
|
+
const verified = withEntry.evidence.some((item) => findingVerificationPasses(finding.data.candidateId, {
|
|
12053
|
+
kind: item.data.kind,
|
|
12054
|
+
candidateId: item.data.candidateId,
|
|
12055
|
+
status: statuses.get(item.id) ?? "missing"
|
|
12056
|
+
}));
|
|
12057
|
+
const contradicted = boundEvidence.result === "failed" && finding.data.refs.some((left) => boundEvidence.refs.some((right) => sameValue(left, right)));
|
|
11960
12058
|
return verified && !contradicted ? finding : { ...finding, data: { ...finding.data, disposition: "open", resolution: null } };
|
|
11961
12059
|
})
|
|
11962
12060
|
});
|
|
@@ -11981,7 +12079,12 @@ class WorkitCore {
|
|
|
11981
12079
|
const input = parsed.data;
|
|
11982
12080
|
if (nativeRequired && input.action !== "record")
|
|
11983
12081
|
return failure("permission_denied", "native observation is only valid for recording decisions");
|
|
11984
|
-
|
|
12082
|
+
const stated = input.action === "record" && input.response === "stated";
|
|
12083
|
+
if (stated && input.purpose === "action")
|
|
12084
|
+
return failure("permission_denied", "stated choices cannot authorize mutating actions");
|
|
12085
|
+
if (stated && !input.binding?.statedChoice)
|
|
12086
|
+
return failure("invalid_input", "stated choices require binding.statedChoice");
|
|
12087
|
+
if (nativeRequired && nativeObservation === undefined && !stated)
|
|
11985
12088
|
return failure("permission_denied", "native decision observation is required");
|
|
11986
12089
|
const task = this.store.readTask(input.taskId);
|
|
11987
12090
|
if (!task.ok)
|
|
@@ -12014,7 +12117,7 @@ class WorkitCore {
|
|
|
12014
12117
|
consumption: null
|
|
12015
12118
|
};
|
|
12016
12119
|
const data = { ...base, digest: decisionDigest(base) };
|
|
12017
|
-
const native = nativeRequired ? verifyNativeDecision(this.context.nativeAuthority, {
|
|
12120
|
+
const native = nativeRequired && !stated ? verifyNativeDecision(this.context.nativeAuthority, {
|
|
12018
12121
|
observation: nativeObservation,
|
|
12019
12122
|
expected: {
|
|
12020
12123
|
taskId: task.data.id,
|
|
@@ -12109,7 +12212,7 @@ class WorkitCore {
|
|
|
12109
12212
|
if (input.action === "record") {
|
|
12110
12213
|
if (helper?.ok) {
|
|
12111
12214
|
const assignment = helper.data.data.assignment;
|
|
12112
|
-
if (!bindingCovers(assignment.scope, input.scope) || !bindingCovers(task.data.intent.data.scope, input.scope) || assignment
|
|
12215
|
+
if (!bindingCovers(assignment.scope, input.scope) || !bindingCovers(task.data.intent.data.scope, input.scope) || !checkPin(assignment, null, input.candidateId ?? null) || !refsWithinScope(input.refs, assignment.scope) || !refsWithinScope(input.refs, task.data.intent.data.scope))
|
|
12113
12216
|
return failure("permission_denied", "finding is outside the worker assignment");
|
|
12114
12217
|
}
|
|
12115
12218
|
if (input.candidateId && !task.data.candidates.some((candidate) => candidate.id === input.candidateId))
|
|
@@ -12156,7 +12259,11 @@ class WorkitCore {
|
|
|
12156
12259
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
12157
12260
|
const verified = evidence.some((entry) => {
|
|
12158
12261
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
12159
|
-
return
|
|
12262
|
+
return findingVerificationPasses(finding.data.candidateId, {
|
|
12263
|
+
kind: entry.data.kind,
|
|
12264
|
+
candidateId: entry.data.candidateId,
|
|
12265
|
+
status: evaluation?.status ?? "missing"
|
|
12266
|
+
});
|
|
12160
12267
|
});
|
|
12161
12268
|
if (!verified)
|
|
12162
12269
|
return failure("permission_denied", "fixed findings require passing verification evidence");
|
|
@@ -12168,7 +12275,7 @@ class WorkitCore {
|
|
|
12168
12275
|
if (entry.data.result === "missing" || entry.data.result === "skipped")
|
|
12169
12276
|
return false;
|
|
12170
12277
|
const candidateMatches = finding.data.candidateId === null ? entry.data.candidateId === null : entry.data.candidateId === finding.data.candidateId || entry.data.beforeCandidateId === finding.data.candidateId;
|
|
12171
|
-
const referenceMatches = finding.data.refs.some((left) => entry.data.refs.some((right) =>
|
|
12278
|
+
const referenceMatches = finding.data.refs.some((left) => entry.data.refs.some((right) => sameValue(left, right)));
|
|
12172
12279
|
return candidateMatches && (referenceMatches || entry.data.claim === finding.data.claim);
|
|
12173
12280
|
});
|
|
12174
12281
|
if (!relevant)
|
|
@@ -12246,7 +12353,7 @@ class WorkitCore {
|
|
|
12246
12353
|
const workerId = newId();
|
|
12247
12354
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12248
12355
|
taskId: task.data.id,
|
|
12249
|
-
|
|
12356
|
+
expectedRevision: input.expectedRevision,
|
|
12250
12357
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12251
12358
|
now: trustedNow(this.context),
|
|
12252
12359
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12284,7 +12391,7 @@ class WorkitCore {
|
|
|
12284
12391
|
return failure("permission_denied", "worker report is outside the worker assignment");
|
|
12285
12392
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12286
12393
|
taskId: task.data.id,
|
|
12287
|
-
|
|
12394
|
+
expectedRevision: input.expectedRevision,
|
|
12288
12395
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12289
12396
|
now: trustedNow(this.context),
|
|
12290
12397
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12308,24 +12415,25 @@ class WorkitCore {
|
|
|
12308
12415
|
return failure("invalid_transition", "closed task cannot cancel workers");
|
|
12309
12416
|
if (entry.data.state === "unknown")
|
|
12310
12417
|
return failure("recovery_required", "worker state requires recovery");
|
|
12311
|
-
if (entry.data.state === "stopped")
|
|
12312
|
-
|
|
12313
|
-
|
|
12418
|
+
if (entry.data.state === "stopped") {
|
|
12419
|
+
const stopped = task.data.workers.find((candidate) => candidate.id === input.workerId);
|
|
12420
|
+
return success(task.data.revision, workspace.data.revision, stopped);
|
|
12421
|
+
}
|
|
12314
12422
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12315
12423
|
taskId: task.data.id,
|
|
12316
|
-
|
|
12424
|
+
expectedRevision: input.expectedRevision,
|
|
12317
12425
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12318
12426
|
now: trustedNow(this.context),
|
|
12319
|
-
workspace: (current, mutation) => success(mutation.revision, mutation.revision,
|
|
12427
|
+
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
12428
|
+
...current,
|
|
12429
|
+
writer: current.writer?.owner.workerId === input.workerId ? null : current.writer
|
|
12430
|
+
}),
|
|
12320
12431
|
task: (current, mutation) => success(mutation.revision, null, {
|
|
12321
12432
|
...current,
|
|
12322
12433
|
workers: current.workers.map((candidate) => candidate.id === input.workerId ? {
|
|
12323
12434
|
...candidate,
|
|
12324
12435
|
recordedAt: mutation.now,
|
|
12325
|
-
data: {
|
|
12326
|
-
...candidate.data,
|
|
12327
|
-
state: settles ? "stopped" : "cancelling"
|
|
12328
|
-
}
|
|
12436
|
+
data: { ...candidate.data, state: "stopped" }
|
|
12329
12437
|
} : candidate)
|
|
12330
12438
|
})
|
|
12331
12439
|
});
|
|
@@ -12422,7 +12530,7 @@ class WorkitCore {
|
|
|
12422
12530
|
return attested;
|
|
12423
12531
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12424
12532
|
taskId: task.data.id,
|
|
12425
|
-
|
|
12533
|
+
expectedRevision: input.expectedRevision,
|
|
12426
12534
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12427
12535
|
now: trustedNow(this.context),
|
|
12428
12536
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12588,6 +12696,12 @@ class WorkitCore {
|
|
|
12588
12696
|
if (!sameSession2(helper.data.data.session, this.context))
|
|
12589
12697
|
return failure("permission_denied", "worker session does not match the caller");
|
|
12590
12698
|
}
|
|
12699
|
+
const gate = this.view(task.data);
|
|
12700
|
+
if (gate.ok) {
|
|
12701
|
+
const writeGates = (task.data.policy?.requirements ?? []).filter((requirement) => requirement.before === "write" && gate.data.requirements.some((evaluation) => evaluation.requirementId === requirement.id && (evaluation.status === "unsatisfied" || evaluation.status === "unavailable")));
|
|
12702
|
+
if (writeGates.length)
|
|
12703
|
+
return failure("requirements_unsatisfied", "writer ownership is gated by unsatisfied requirements; record their evidence or an approved limitation waiver first", { requirementIds: writeGates.map((requirement) => requirement.id) });
|
|
12704
|
+
}
|
|
12591
12705
|
const owner = {
|
|
12592
12706
|
taskId: task.data.id,
|
|
12593
12707
|
workerId: helperId,
|
|
@@ -12599,7 +12713,7 @@ class WorkitCore {
|
|
|
12599
12713
|
};
|
|
12600
12714
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12601
12715
|
taskId: task.data.id,
|
|
12602
|
-
|
|
12716
|
+
expectedRevision: input.expectedRevision,
|
|
12603
12717
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12604
12718
|
now: trustedNow(this.context),
|
|
12605
12719
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
@@ -12630,7 +12744,7 @@ class WorkitCore {
|
|
|
12630
12744
|
}
|
|
12631
12745
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12632
12746
|
taskId: task.data.id,
|
|
12633
|
-
|
|
12747
|
+
expectedRevision: input.expectedRevision,
|
|
12634
12748
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12635
12749
|
now: trustedNow(this.context),
|
|
12636
12750
|
workspace: (currentWorkspace, mutation) => success(mutation.revision, mutation.revision, { ...currentWorkspace, writer: null }),
|
|
@@ -12879,7 +12993,15 @@ class WorkitCore {
|
|
|
12879
12993
|
if (seen.has(observation.workerId))
|
|
12880
12994
|
return failure("invalid_input", "worker observations must be unique");
|
|
12881
12995
|
seen.add(observation.workerId);
|
|
12882
|
-
|
|
12996
|
+
const freshTask = this.store.readTask(view.task.id);
|
|
12997
|
+
const freshWorkspace = this.store.readWorkspace();
|
|
12998
|
+
if (!freshTask.ok)
|
|
12999
|
+
return freshTask;
|
|
13000
|
+
if (!freshWorkspace.ok)
|
|
13001
|
+
return freshWorkspace;
|
|
13002
|
+
if (!freshWorkspace.data)
|
|
13003
|
+
return failure("not_found", "workspace not found");
|
|
13004
|
+
if (observation.taskId !== view.task.id || observation.expectedRevision !== freshTask.data.revision || observation.expectedWorkspaceRevision !== freshWorkspace.data.revision)
|
|
12883
13005
|
return failure("revision_conflict", "worker observation is stale");
|
|
12884
13006
|
if (!view.task.workers.some((entry) => entry.id === observation.workerId))
|
|
12885
13007
|
return failure("permission_denied", "worker observation is not assigned");
|
|
@@ -12965,19 +13087,26 @@ class WorkitCore {
|
|
|
12965
13087
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
12966
13088
|
resumeCandidate = current.data.candidate;
|
|
12967
13089
|
}
|
|
12968
|
-
const blocker = this.activeWorkerBlocker(task.data, workspace.data);
|
|
13090
|
+
const blocker = status === "paused" ? workspace.data.writer ? failure("recovery_required", "writer ownership must be released first") : null : this.activeWorkerBlocker(task.data, workspace.data);
|
|
12969
13091
|
if (blocker)
|
|
12970
13092
|
return blocker;
|
|
13093
|
+
let pauseCandidate = null;
|
|
13094
|
+
if (status === "paused") {
|
|
13095
|
+
const captured = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
13096
|
+
if (!captured.ok)
|
|
13097
|
+
return captured;
|
|
13098
|
+
pauseCandidate = captured.data;
|
|
13099
|
+
}
|
|
12971
13100
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12972
13101
|
taskId: task.data.id,
|
|
12973
|
-
|
|
13102
|
+
expectedRevision: input.expectedRevision,
|
|
12974
13103
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12975
13104
|
now: trustedNow(this.context),
|
|
12976
13105
|
workspace: (workspace, context) => success(context.revision, context.revision, workspace),
|
|
12977
13106
|
task: (current, context) => success(context.revision, null, {
|
|
12978
13107
|
...current,
|
|
12979
13108
|
status,
|
|
12980
|
-
candidates: resumeCandidate && !current.candidates.some((candidate) => candidate.id === resumeCandidate.id) ? [...current.candidates, resumeCandidate] : current.candidates,
|
|
13109
|
+
candidates: (resumeCandidate ?? pauseCandidate) && !current.candidates.some((candidate) => candidate.id === (resumeCandidate ?? pauseCandidate).id) ? [...current.candidates, resumeCandidate ?? pauseCandidate] : current.candidates,
|
|
12981
13110
|
progress: current.progress,
|
|
12982
13111
|
pauseReason: status === "paused" ? input.reason ?? null : null
|
|
12983
13112
|
})
|
|
@@ -13015,12 +13144,13 @@ class WorkitCore {
|
|
|
13015
13144
|
return workspace;
|
|
13016
13145
|
if (!workspace.data)
|
|
13017
13146
|
return failure("not_found", "workspace not found");
|
|
13018
|
-
|
|
13019
|
-
|
|
13147
|
+
const revisionBlocker = this.activeWorkerBlocker(task.data, workspace.data);
|
|
13148
|
+
if (revisionBlocker)
|
|
13149
|
+
return revisionBlocker;
|
|
13020
13150
|
this.fillRevisions(input, task.data, workspace.data);
|
|
13021
13151
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
13022
13152
|
taskId: task.data.id,
|
|
13023
|
-
|
|
13153
|
+
expectedRevision: input.expectedRevision,
|
|
13024
13154
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
13025
13155
|
now: trustedNow(this.context),
|
|
13026
13156
|
workspace: (workspace, context) => success(context.revision, context.revision, workspace),
|
|
@@ -13076,7 +13206,7 @@ class WorkitCore {
|
|
|
13076
13206
|
return closure;
|
|
13077
13207
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
13078
13208
|
taskId: task.data.id,
|
|
13079
|
-
|
|
13209
|
+
expectedRevision: input.expectedRevision,
|
|
13080
13210
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
13081
13211
|
now: trustedNow(this.context),
|
|
13082
13212
|
workspace: (value, context) => success(context.revision, context.revision, value),
|
|
@@ -13167,7 +13297,7 @@ var cursorCapabilities = (availability = {}) => {
|
|
|
13167
13297
|
}
|
|
13168
13298
|
];
|
|
13169
13299
|
};
|
|
13170
|
-
var
|
|
13300
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13171
13301
|
var eventNames = new Set([
|
|
13172
13302
|
"sessionStart",
|
|
13173
13303
|
"preToolUse",
|
|
@@ -13178,7 +13308,7 @@ var eventNames = new Set([
|
|
|
13178
13308
|
]);
|
|
13179
13309
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
13180
13310
|
var parseCursorHookInput = (value) => {
|
|
13181
|
-
if (!
|
|
13311
|
+
if (!isRecord2(value) || !eventNames.has(value.hook_event_name))
|
|
13182
13312
|
return { ok: false, error: "hook_event_name is required" };
|
|
13183
13313
|
const roots = value.workspace_roots;
|
|
13184
13314
|
if (!Array.isArray(roots) || roots.length !== 1 || !roots.every(nonEmpty2))
|
|
@@ -13325,7 +13455,7 @@ var handleSubagentStop = () => ({});
|
|
|
13325
13455
|
var handleCursorHook = (raw) => {
|
|
13326
13456
|
const parsed = parseCursorHookInput(raw);
|
|
13327
13457
|
if (!parsed.ok) {
|
|
13328
|
-
return
|
|
13458
|
+
return isRecord2(raw) && raw.hook_event_name === "sessionStart" ? {} : deny(parsed.error);
|
|
13329
13459
|
}
|
|
13330
13460
|
const input = parsed.data;
|
|
13331
13461
|
const root = realpathSync5(input.workspace_roots[0]);
|
|
@@ -13370,7 +13500,7 @@ var runCursorHook = async () => {
|
|
|
13370
13500
|
const output = handleCursorHook(input);
|
|
13371
13501
|
process.stdout.write(`${JSON.stringify(output)}
|
|
13372
13502
|
`);
|
|
13373
|
-
const event =
|
|
13503
|
+
const event = isRecord2(input) ? input.hook_event_name : undefined;
|
|
13374
13504
|
if (output.permission === "deny" && ["preToolUse", "beforeShellExecution", "subagentStart"].includes(String(event)))
|
|
13375
13505
|
process.exitCode = 2;
|
|
13376
13506
|
} catch {
|