@brainervirus/workit-cursor 1.0.11 → 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 +363 -212
- package/dist/mcp-server.js +2973 -2869
- package/dist/workit-hook.js +334 -210
- 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,
|
|
@@ -9005,11 +9088,11 @@ class TaskStore {
|
|
|
9005
9088
|
return workspace;
|
|
9006
9089
|
if (!workspace.data)
|
|
9007
9090
|
return failure("not_found", "workspace not found");
|
|
9008
|
-
const
|
|
9009
|
-
if (!
|
|
9091
|
+
const expected = input.expectedRevision;
|
|
9092
|
+
if (!expected)
|
|
9010
9093
|
return failure("invalid_input", "task revision is required");
|
|
9011
|
-
if (task.data.revision !==
|
|
9012
|
-
return this.conflict(
|
|
9094
|
+
if (task.data.revision !== expected)
|
|
9095
|
+
return this.conflict(expected, task.data.revision);
|
|
9013
9096
|
if (workspace.data.revision !== input.expectedWorkspaceRevision)
|
|
9014
9097
|
return this.conflict(input.expectedWorkspaceRevision, workspace.data.revision);
|
|
9015
9098
|
const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
|
|
@@ -9543,6 +9626,7 @@ var same = (left, right) => {
|
|
|
9543
9626
|
};
|
|
9544
9627
|
var UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
|
|
9545
9628
|
var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
9629
|
+
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
9546
9630
|
var callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor };
|
|
9547
9631
|
var validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
|
|
9548
9632
|
const code = char.charCodeAt(0);
|
|
@@ -9604,6 +9688,36 @@ var trustedAuthority = (kind, provenance, expected, binding, caller) => {
|
|
|
9604
9688
|
verifiedAuthorities.set(token, { kind, provenance, ...expected, ...binding, caller });
|
|
9605
9689
|
return token;
|
|
9606
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
|
+
};
|
|
9607
9721
|
var scopeEqual = (left, right) => canonicalJson(left) === canonicalJson(right);
|
|
9608
9722
|
var parseSteps = (content) => {
|
|
9609
9723
|
let value;
|
|
@@ -9615,7 +9729,10 @@ var parseSteps = (content) => {
|
|
|
9615
9729
|
if (typeof value !== "object" || value === null || !("steps" in value))
|
|
9616
9730
|
return [];
|
|
9617
9731
|
const steps = value.steps;
|
|
9618
|
-
|
|
9732
|
+
if (!Array.isArray(steps))
|
|
9733
|
+
return null;
|
|
9734
|
+
const normalized = normalizeChainSteps(steps);
|
|
9735
|
+
return normalized;
|
|
9619
9736
|
};
|
|
9620
9737
|
var decisionMatches = (entry, purpose, binding) => {
|
|
9621
9738
|
const decision = entry.data;
|
|
@@ -9806,14 +9923,12 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
9806
9923
|
return content;
|
|
9807
9924
|
const requestedSteps = parseSteps(decision.binding.approvedContent);
|
|
9808
9925
|
if (requestedSteps === null)
|
|
9809
|
-
return failure("invalid_input", "bounded action steps must be a
|
|
9810
|
-
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)))
|
|
9811
9928
|
return failure("permission_denied", "bounded action steps do not match approved content");
|
|
9812
|
-
if (requestedSteps.some((step) => !step || typeof step !== "string") || new Set(requestedSteps).size !== requestedSteps.length)
|
|
9813
|
-
return failure("invalid_input", "bounded action steps must be unique and non-empty");
|
|
9814
9929
|
const stored = task.actionProgress?.find((item) => item.decisionId === input.decisionId);
|
|
9815
|
-
const workflow = stored ? { steps: stored.steps, completed: stored.completedSteps } : { steps: requestedSteps, completed: [] };
|
|
9816
|
-
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)))
|
|
9817
9932
|
return failure("permission_denied", "bounded action scope changed");
|
|
9818
9933
|
return success(null, null, { entry, workflow });
|
|
9819
9934
|
};
|
|
@@ -10368,13 +10483,28 @@ function evaluateEvidence(task, candidate) {
|
|
|
10368
10483
|
});
|
|
10369
10484
|
}
|
|
10370
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);
|
|
10371
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")));
|
|
10372
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 }));
|
|
10373
|
-
var evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
|
|
10503
|
+
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
10374
10504
|
if (kind !== "passed")
|
|
10375
10505
|
return false;
|
|
10376
10506
|
if (dimension === "testing" || dimension === "verification")
|
|
10377
|
-
return evidenceKind === "check";
|
|
10507
|
+
return evidenceKind === "check" || ruleId === "pre-pr-cleanup" && evidenceKind === "artifact";
|
|
10378
10508
|
if (dimension === "review")
|
|
10379
10509
|
return evidenceKind === "review";
|
|
10380
10510
|
if (dimension === "investigation" || dimension === "challenge")
|
|
@@ -10383,6 +10513,21 @@ var evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
|
|
|
10383
10513
|
return evidenceKind === "artifact";
|
|
10384
10514
|
return false;
|
|
10385
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
|
+
};
|
|
10386
10531
|
function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller) {
|
|
10387
10532
|
const evidence = evaluateEvidence(task, candidate);
|
|
10388
10533
|
if (task.policy && task.policy.policyVersion !== POLICY_VERSION)
|
|
@@ -10396,7 +10541,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10396
10541
|
return (task.policy?.requirements ?? []).map((requirement) => {
|
|
10397
10542
|
const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
|
|
10398
10543
|
const passed = related.filter(({ entry, evaluation }) => {
|
|
10399
|
-
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension))
|
|
10544
|
+
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
|
|
10400
10545
|
return false;
|
|
10401
10546
|
if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
|
|
10402
10547
|
return false;
|
|
@@ -10404,11 +10549,12 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10404
10549
|
return true;
|
|
10405
10550
|
const reviewSession = sessionFromRef(entry.data.reviewContext);
|
|
10406
10551
|
const sameImplementation = sameSession(reviewSession, task.intent.provenance.session);
|
|
10407
|
-
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));
|
|
10408
10553
|
return entry.data.kind === "review" && reviewSession !== null && sameSession(reviewSession, entry.provenance.session) && (requirement.ruleId === "self-review" || !sameImplementation && !sameEvidenceSession);
|
|
10409
10554
|
});
|
|
10410
10555
|
if (passed.length) {
|
|
10411
|
-
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;
|
|
10412
10558
|
if (withRed.length)
|
|
10413
10559
|
return {
|
|
10414
10560
|
requirementId: requirement.id,
|
|
@@ -10457,6 +10603,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10457
10603
|
reason: "an applicable approved limitation permits the missing evidence"
|
|
10458
10604
|
};
|
|
10459
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
|
+
};
|
|
10460
10614
|
return {
|
|
10461
10615
|
requirementId: requirement.id,
|
|
10462
10616
|
status: unavailable ? "unavailable" : "unsatisfied",
|
|
@@ -10481,7 +10635,13 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10481
10635
|
if (entry.data.disposition === "fixed") {
|
|
10482
10636
|
const verified = entry.data.resolution?.evidenceIds.some((id) => {
|
|
10483
10637
|
const evidence = view.task.evidence.find((item) => item.id === id);
|
|
10484
|
-
|
|
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
|
+
});
|
|
10485
10645
|
});
|
|
10486
10646
|
if (!verified)
|
|
10487
10647
|
return failure("requirements_unsatisfied", "fixed findings require current verification");
|
|
@@ -10497,7 +10657,12 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10497
10657
|
}
|
|
10498
10658
|
}
|
|
10499
10659
|
}
|
|
10500
|
-
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
|
+
});
|
|
10501
10666
|
if (requestedOutcome !== "stopped" && blocking.length)
|
|
10502
10667
|
return failure("requirements_unsatisfied", "applicable requirements are unsatisfied", {
|
|
10503
10668
|
requirementIds: blocking.map((item) => item.requirementId),
|
|
@@ -10540,15 +10705,15 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10540
10705
|
// packages/workit-core/src/core/methods.ts
|
|
10541
10706
|
var METHODS = {
|
|
10542
10707
|
"workit-challenge": { dimensions: ["challenge", "decisions"] },
|
|
10543
|
-
"workit-behavioral-tdd": { dimensions: ["testing"] },
|
|
10544
|
-
"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"] },
|
|
10545
10710
|
"workit-plan": { dimensions: ["artifacts", "continuity"] },
|
|
10546
10711
|
"workit-implement": { dimensions: ["delegation"] },
|
|
10547
|
-
"workit-debug": {
|
|
10548
|
-
"workit-handoff": {
|
|
10712
|
+
"workit-debug": {},
|
|
10713
|
+
"workit-handoff": {},
|
|
10549
10714
|
"workit-deslop": { ruleIds: ["pre-pr-cleanup"] }
|
|
10550
10715
|
};
|
|
10551
|
-
var methodMatches = (
|
|
10716
|
+
var methodMatches = (definition, requirement) => definition.dimensions?.includes(requirement.dimension) === true || definition.ruleIds?.includes(requirement.ruleId) === true;
|
|
10552
10717
|
var assuranceFor = (id, definition, capabilities) => {
|
|
10553
10718
|
const keys = new Set([
|
|
10554
10719
|
id,
|
|
@@ -10569,7 +10734,7 @@ var assuranceFor = (id, definition, capabilities) => {
|
|
|
10569
10734
|
};
|
|
10570
10735
|
function selectMethods(policy, capabilities) {
|
|
10571
10736
|
return Object.entries(METHODS).flatMap(([id, definition]) => {
|
|
10572
|
-
const requirements = policy.requirements.filter((requirement) => methodMatches(
|
|
10737
|
+
const requirements = policy.requirements.filter((requirement) => methodMatches(definition, requirement));
|
|
10573
10738
|
if (requirements.length === 0)
|
|
10574
10739
|
return [];
|
|
10575
10740
|
const capability = assuranceFor(id, definition, capabilities);
|
|
@@ -10737,6 +10902,27 @@ var exportDigest = (bundle) => sha256({
|
|
|
10737
10902
|
sourceWorkspaceId: bundle.sourceWorkspaceId,
|
|
10738
10903
|
task: bundle.task
|
|
10739
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
|
+
};
|
|
10740
10926
|
// packages/workit-core/src/core/external-action.ts
|
|
10741
10927
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
10742
10928
|
object({
|
|
@@ -11274,6 +11460,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11274
11460
|
if (task.data.status === "paused" && (input.state === "running" || input.state === "cancelling"))
|
|
11275
11461
|
return failure("invalid_transition", "paused task cannot run a worker");
|
|
11276
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);
|
|
11277
11465
|
if (notStarted && (input.state !== "stopped" || input.session !== null))
|
|
11278
11466
|
return failure("invalid_input", "a never-dispatched worker stops with no session");
|
|
11279
11467
|
if (notStarted && entry.data.session !== null)
|
|
@@ -11305,7 +11493,7 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11305
11493
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
11306
11494
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
11307
11495
|
taskId: task.data.id,
|
|
11308
|
-
|
|
11496
|
+
expectedRevision: input.expectedRevision,
|
|
11309
11497
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
11310
11498
|
now: input.now,
|
|
11311
11499
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
@@ -11353,90 +11541,22 @@ var importedProvenance = (context) => ({
|
|
|
11353
11541
|
receipts: []
|
|
11354
11542
|
});
|
|
11355
11543
|
var portableTask = (task) => {
|
|
11356
|
-
const
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
|
|
11365
|
-
|
|
11366
|
-
|
|
11367
|
-
|
|
11368
|
-
|
|
11369
|
-
];
|
|
11370
|
-
for (const entry of entries)
|
|
11371
|
-
entry.provenance = portableProvenance(entry.provenance);
|
|
11372
|
-
for (const worker of clone.workers)
|
|
11373
|
-
worker.data.session = null;
|
|
11374
|
-
clone.intent = {
|
|
11375
|
-
...clone.intent,
|
|
11376
|
-
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));
|
|
11377
11557
|
};
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
return source ? [
|
|
11381
|
-
{
|
|
11382
|
-
...constraint,
|
|
11383
|
-
source,
|
|
11384
|
-
requires: constraint.requires.map((requirement) => ({
|
|
11385
|
-
...requirement,
|
|
11386
|
-
refs: portableRefs(requirement.refs)
|
|
11387
|
-
}))
|
|
11388
|
-
}
|
|
11389
|
-
] : [];
|
|
11390
|
-
});
|
|
11391
|
-
clone.assessments = clone.assessments.map((entry) => ({
|
|
11392
|
-
...entry,
|
|
11393
|
-
data: {
|
|
11394
|
-
...entry.data,
|
|
11395
|
-
facts: entry.data.facts.map(portableFact),
|
|
11396
|
-
signals: Object.fromEntries(Object.entries(entry.data.signals).map(([name, signal]) => [name, portableSignal(signal)])),
|
|
11397
|
-
consequences: entry.data.consequences.map((consequence) => ({
|
|
11398
|
-
...consequence,
|
|
11399
|
-
fact: portableFact(consequence.fact)
|
|
11400
|
-
})),
|
|
11401
|
-
verification: entry.data.verification.map((verification) => ({
|
|
11402
|
-
...verification,
|
|
11403
|
-
availableChecks: portableRefs(verification.availableChecks)
|
|
11404
|
-
}))
|
|
11405
|
-
}
|
|
11406
|
-
}));
|
|
11407
|
-
clone.progress = {
|
|
11408
|
-
...clone.progress,
|
|
11409
|
-
blockers: clone.progress.blockers.map((blocker) => ({
|
|
11410
|
-
...blocker,
|
|
11411
|
-
refs: portableRefs(blocker.refs)
|
|
11412
|
-
}))
|
|
11413
|
-
};
|
|
11414
|
-
clone.evidence = clone.evidence.map((entry) => ({
|
|
11415
|
-
...entry,
|
|
11416
|
-
data: {
|
|
11417
|
-
...entry.data,
|
|
11418
|
-
refs: portableRefs(entry.data.refs),
|
|
11419
|
-
reviewContext: portableRef(entry.data.reviewContext)
|
|
11420
|
-
}
|
|
11421
|
-
}));
|
|
11422
|
-
clone.decisions = clone.decisions.map((entry) => ({
|
|
11423
|
-
...entry,
|
|
11424
|
-
data: {
|
|
11425
|
-
...entry.data,
|
|
11426
|
-
consumption: entry.data.consumption ? portableRef(entry.data.consumption.actionRef) ? {
|
|
11427
|
-
...entry.data.consumption,
|
|
11428
|
-
actionRef: portableRef(entry.data.consumption.actionRef)
|
|
11429
|
-
} : null : null,
|
|
11430
|
-
binding: {
|
|
11431
|
-
...entry.data.binding,
|
|
11432
|
-
contentRefs: portableRefs(entry.data.binding.contentRefs)
|
|
11433
|
-
}
|
|
11434
|
-
}
|
|
11435
|
-
}));
|
|
11436
|
-
clone.findings = clone.findings.map((entry) => ({
|
|
11437
|
-
...entry,
|
|
11438
|
-
data: { ...entry.data, refs: portableRefs(entry.data.refs) }
|
|
11439
|
-
}));
|
|
11558
|
+
for (const entry of clone.assessments)
|
|
11559
|
+
demote(entry.data.facts, entry.data.signals, entry.data.consequences);
|
|
11440
11560
|
clone.candidates = [];
|
|
11441
11561
|
return clone;
|
|
11442
11562
|
};
|
|
@@ -11461,15 +11581,13 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11461
11581
|
allocate(entry.id);
|
|
11462
11582
|
const fresh = (id) => allocate(id);
|
|
11463
11583
|
const provenance = importedProvenance(context);
|
|
11584
|
+
const remap = (ref) => mapRef(ref, ids);
|
|
11464
11585
|
const intent = {
|
|
11465
11586
|
...source.intent,
|
|
11466
11587
|
id: fresh(source.intent.id),
|
|
11467
11588
|
recordedAt: timestamp,
|
|
11468
11589
|
provenance,
|
|
11469
|
-
data:
|
|
11470
|
-
...source.intent.data,
|
|
11471
|
-
authorityRefs: source.intent.data.authorityRefs.map((ref) => mapRef(ref, ids))
|
|
11472
|
-
}
|
|
11590
|
+
data: rewriteRecordRefs(source.intent.data, intentSchema, remap)
|
|
11473
11591
|
};
|
|
11474
11592
|
const assessments = source.assessments.map((entry) => {
|
|
11475
11593
|
const data = entry.data;
|
|
@@ -11478,28 +11596,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11478
11596
|
id: fresh(entry.id),
|
|
11479
11597
|
recordedAt: timestamp,
|
|
11480
11598
|
provenance,
|
|
11481
|
-
data:
|
|
11482
|
-
...data,
|
|
11483
|
-
facts: data.facts.map((fact) => ({
|
|
11484
|
-
...fact,
|
|
11485
|
-
refs: fact.refs.map((ref) => mapRef(ref, ids))
|
|
11486
|
-
})),
|
|
11487
|
-
signals: Object.fromEntries(Object.entries(data.signals).map(([name, signal]) => [
|
|
11488
|
-
name,
|
|
11489
|
-
{ ...signal, refs: signal.refs.map((ref) => mapRef(ref, ids)) }
|
|
11490
|
-
])),
|
|
11491
|
-
consequences: data.consequences.map((consequence) => ({
|
|
11492
|
-
...consequence,
|
|
11493
|
-
fact: {
|
|
11494
|
-
...consequence.fact,
|
|
11495
|
-
refs: consequence.fact.refs.map((ref) => mapRef(ref, ids))
|
|
11496
|
-
}
|
|
11497
|
-
})),
|
|
11498
|
-
verification: data.verification.map((verification) => ({
|
|
11499
|
-
...verification,
|
|
11500
|
-
availableChecks: verification.availableChecks.map((ref) => mapRef(ref, ids))
|
|
11501
|
-
}))
|
|
11502
|
-
}
|
|
11599
|
+
data: rewriteRecordRefs(data, assessmentSchema, remap)
|
|
11503
11600
|
};
|
|
11504
11601
|
});
|
|
11505
11602
|
const evidence = source.evidence.map((entry) => ({
|
|
@@ -11507,11 +11604,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11507
11604
|
id: fresh(entry.id),
|
|
11508
11605
|
recordedAt: timestamp,
|
|
11509
11606
|
provenance,
|
|
11510
|
-
data:
|
|
11511
|
-
...entry.data,
|
|
11512
|
-
refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
|
|
11513
|
-
reviewContext: entry.data.reviewContext ? mapRef(entry.data.reviewContext, ids) : null
|
|
11514
|
-
}
|
|
11607
|
+
data: rewriteRecordRefs(entry.data, evidenceSchema, remap)
|
|
11515
11608
|
}));
|
|
11516
11609
|
const decisions = source.decisions.map((entry) => ({
|
|
11517
11610
|
...entry,
|
|
@@ -11519,21 +11612,23 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11519
11612
|
recordedAt: timestamp,
|
|
11520
11613
|
provenance
|
|
11521
11614
|
}));
|
|
11522
|
-
const findings = source.findings.map((entry) =>
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
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
|
+
});
|
|
11537
11632
|
const workers = source.workers.map((entry) => ({
|
|
11538
11633
|
...entry,
|
|
11539
11634
|
id: fresh(entry.id),
|
|
@@ -11576,19 +11671,21 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11576
11671
|
closure: null,
|
|
11577
11672
|
assessments,
|
|
11578
11673
|
evidence,
|
|
11579
|
-
decisions: decisions.map((entry) =>
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
...entry
|
|
11583
|
-
|
|
11584
|
-
...
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
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
|
+
}),
|
|
11592
11689
|
...actionProgress ? { actionProgress } : {},
|
|
11593
11690
|
findings,
|
|
11594
11691
|
workers,
|
|
@@ -11671,7 +11768,7 @@ class WorkitCore {
|
|
|
11671
11768
|
activeWorkerBlocker(task, workspace) {
|
|
11672
11769
|
if (workspace.writer)
|
|
11673
11770
|
return failure("recovery_required", "writer ownership must be released first");
|
|
11674
|
-
if (task.workers.some((entry) =>
|
|
11771
|
+
if (task.workers.some((entry) => workerBlocksTransition("close", entry.data.state)))
|
|
11675
11772
|
return failure("recovery_required", "worker state requires reconciliation");
|
|
11676
11773
|
return null;
|
|
11677
11774
|
}
|
|
@@ -11917,37 +12014,33 @@ class WorkitCore {
|
|
|
11917
12014
|
const currentCandidate = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
11918
12015
|
if (!currentCandidate.ok)
|
|
11919
12016
|
return currentCandidate;
|
|
11920
|
-
|
|
11921
|
-
|
|
11922
|
-
evidence.candidateId ??= currentCandidate.data.id;
|
|
11923
|
-
} else {
|
|
11924
|
-
evidence.beforeCandidateId ??= null;
|
|
11925
|
-
evidence.candidateId ??= null;
|
|
11926
|
-
}
|
|
12017
|
+
const pin = helper && helper.ok ? helper.data.data.assignment.candidateId : null;
|
|
12018
|
+
const bound = resolveBinding(evidence, pin, currentCandidate.data.id);
|
|
11927
12019
|
if (helper?.ok) {
|
|
11928
12020
|
const assignment = helper.data.data.assignment;
|
|
11929
|
-
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))
|
|
11930
12022
|
return failure("permission_denied", "evidence is outside the worker assignment");
|
|
11931
12023
|
}
|
|
11932
12024
|
const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
|
|
11933
|
-
for (const candidateId of [
|
|
12025
|
+
for (const candidateId of [bound.beforeCandidateId, bound.candidateId]) {
|
|
11934
12026
|
if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
|
|
11935
12027
|
return failure("invalid_input", "evidence candidate is not a captured candidate");
|
|
11936
12028
|
}
|
|
12029
|
+
const boundEvidence = { ...evidence, ...bound };
|
|
11937
12030
|
const changed = this.store.mutateTask(task.data.id, input.expectedRevision, (current, mutation) => {
|
|
11938
12031
|
const candidates = current.candidates.some((item) => item.id === currentCandidate.data.id) ? current.candidates : [...current.candidates, currentCandidate.data];
|
|
11939
12032
|
const entry = {
|
|
11940
12033
|
id: newId(),
|
|
11941
12034
|
recordedAt: mutation.now,
|
|
11942
12035
|
provenance: provenance(this.context, "agent_reported"),
|
|
11943
|
-
data:
|
|
12036
|
+
data: boundEvidence
|
|
11944
12037
|
};
|
|
11945
12038
|
return success(mutation.revision, null, {
|
|
11946
12039
|
...current,
|
|
11947
12040
|
candidates,
|
|
11948
12041
|
evidence: [...current.evidence, entry],
|
|
11949
12042
|
findings: current.findings.map((finding) => {
|
|
11950
|
-
if (finding.data.disposition === "open" ||
|
|
12043
|
+
if (finding.data.disposition === "open" || boundEvidence.result === "skipped")
|
|
11951
12044
|
return finding;
|
|
11952
12045
|
if (finding.data.disposition !== "fixed")
|
|
11953
12046
|
return finding;
|
|
@@ -11956,8 +12049,12 @@ class WorkitCore {
|
|
|
11956
12049
|
item.evidenceId,
|
|
11957
12050
|
item.status
|
|
11958
12051
|
]));
|
|
11959
|
-
const verified = withEntry.evidence.some((item) =>
|
|
11960
|
-
|
|
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)));
|
|
11961
12058
|
return verified && !contradicted ? finding : { ...finding, data: { ...finding.data, disposition: "open", resolution: null } };
|
|
11962
12059
|
})
|
|
11963
12060
|
});
|
|
@@ -12115,7 +12212,7 @@ class WorkitCore {
|
|
|
12115
12212
|
if (input.action === "record") {
|
|
12116
12213
|
if (helper?.ok) {
|
|
12117
12214
|
const assignment = helper.data.data.assignment;
|
|
12118
|
-
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))
|
|
12119
12216
|
return failure("permission_denied", "finding is outside the worker assignment");
|
|
12120
12217
|
}
|
|
12121
12218
|
if (input.candidateId && !task.data.candidates.some((candidate) => candidate.id === input.candidateId))
|
|
@@ -12162,7 +12259,11 @@ class WorkitCore {
|
|
|
12162
12259
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
12163
12260
|
const verified = evidence.some((entry) => {
|
|
12164
12261
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
12165
|
-
return
|
|
12262
|
+
return findingVerificationPasses(finding.data.candidateId, {
|
|
12263
|
+
kind: entry.data.kind,
|
|
12264
|
+
candidateId: entry.data.candidateId,
|
|
12265
|
+
status: evaluation?.status ?? "missing"
|
|
12266
|
+
});
|
|
12166
12267
|
});
|
|
12167
12268
|
if (!verified)
|
|
12168
12269
|
return failure("permission_denied", "fixed findings require passing verification evidence");
|
|
@@ -12174,7 +12275,7 @@ class WorkitCore {
|
|
|
12174
12275
|
if (entry.data.result === "missing" || entry.data.result === "skipped")
|
|
12175
12276
|
return false;
|
|
12176
12277
|
const candidateMatches = finding.data.candidateId === null ? entry.data.candidateId === null : entry.data.candidateId === finding.data.candidateId || entry.data.beforeCandidateId === finding.data.candidateId;
|
|
12177
|
-
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)));
|
|
12178
12279
|
return candidateMatches && (referenceMatches || entry.data.claim === finding.data.claim);
|
|
12179
12280
|
});
|
|
12180
12281
|
if (!relevant)
|
|
@@ -12252,7 +12353,7 @@ class WorkitCore {
|
|
|
12252
12353
|
const workerId = newId();
|
|
12253
12354
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12254
12355
|
taskId: task.data.id,
|
|
12255
|
-
|
|
12356
|
+
expectedRevision: input.expectedRevision,
|
|
12256
12357
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12257
12358
|
now: trustedNow(this.context),
|
|
12258
12359
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12290,7 +12391,7 @@ class WorkitCore {
|
|
|
12290
12391
|
return failure("permission_denied", "worker report is outside the worker assignment");
|
|
12291
12392
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12292
12393
|
taskId: task.data.id,
|
|
12293
|
-
|
|
12394
|
+
expectedRevision: input.expectedRevision,
|
|
12294
12395
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12295
12396
|
now: trustedNow(this.context),
|
|
12296
12397
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12314,24 +12415,25 @@ class WorkitCore {
|
|
|
12314
12415
|
return failure("invalid_transition", "closed task cannot cancel workers");
|
|
12315
12416
|
if (entry.data.state === "unknown")
|
|
12316
12417
|
return failure("recovery_required", "worker state requires recovery");
|
|
12317
|
-
if (entry.data.state === "stopped")
|
|
12318
|
-
|
|
12319
|
-
|
|
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
|
+
}
|
|
12320
12422
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12321
12423
|
taskId: task.data.id,
|
|
12322
|
-
|
|
12424
|
+
expectedRevision: input.expectedRevision,
|
|
12323
12425
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12324
12426
|
now: trustedNow(this.context),
|
|
12325
|
-
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
|
+
}),
|
|
12326
12431
|
task: (current, mutation) => success(mutation.revision, null, {
|
|
12327
12432
|
...current,
|
|
12328
12433
|
workers: current.workers.map((candidate) => candidate.id === input.workerId ? {
|
|
12329
12434
|
...candidate,
|
|
12330
12435
|
recordedAt: mutation.now,
|
|
12331
|
-
data: {
|
|
12332
|
-
...candidate.data,
|
|
12333
|
-
state: settles ? "stopped" : "cancelling"
|
|
12334
|
-
}
|
|
12436
|
+
data: { ...candidate.data, state: "stopped" }
|
|
12335
12437
|
} : candidate)
|
|
12336
12438
|
})
|
|
12337
12439
|
});
|
|
@@ -12428,7 +12530,7 @@ class WorkitCore {
|
|
|
12428
12530
|
return attested;
|
|
12429
12531
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12430
12532
|
taskId: task.data.id,
|
|
12431
|
-
|
|
12533
|
+
expectedRevision: input.expectedRevision,
|
|
12432
12534
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12433
12535
|
now: trustedNow(this.context),
|
|
12434
12536
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12594,6 +12696,12 @@ class WorkitCore {
|
|
|
12594
12696
|
if (!sameSession2(helper.data.data.session, this.context))
|
|
12595
12697
|
return failure("permission_denied", "worker session does not match the caller");
|
|
12596
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
|
+
}
|
|
12597
12705
|
const owner = {
|
|
12598
12706
|
taskId: task.data.id,
|
|
12599
12707
|
workerId: helperId,
|
|
@@ -12605,7 +12713,7 @@ class WorkitCore {
|
|
|
12605
12713
|
};
|
|
12606
12714
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12607
12715
|
taskId: task.data.id,
|
|
12608
|
-
|
|
12716
|
+
expectedRevision: input.expectedRevision,
|
|
12609
12717
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12610
12718
|
now: trustedNow(this.context),
|
|
12611
12719
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
@@ -12636,7 +12744,7 @@ class WorkitCore {
|
|
|
12636
12744
|
}
|
|
12637
12745
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12638
12746
|
taskId: task.data.id,
|
|
12639
|
-
|
|
12747
|
+
expectedRevision: input.expectedRevision,
|
|
12640
12748
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12641
12749
|
now: trustedNow(this.context),
|
|
12642
12750
|
workspace: (currentWorkspace, mutation) => success(mutation.revision, mutation.revision, { ...currentWorkspace, writer: null }),
|
|
@@ -12885,7 +12993,15 @@ class WorkitCore {
|
|
|
12885
12993
|
if (seen.has(observation.workerId))
|
|
12886
12994
|
return failure("invalid_input", "worker observations must be unique");
|
|
12887
12995
|
seen.add(observation.workerId);
|
|
12888
|
-
|
|
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)
|
|
12889
13005
|
return failure("revision_conflict", "worker observation is stale");
|
|
12890
13006
|
if (!view.task.workers.some((entry) => entry.id === observation.workerId))
|
|
12891
13007
|
return failure("permission_denied", "worker observation is not assigned");
|
|
@@ -12971,19 +13087,26 @@ class WorkitCore {
|
|
|
12971
13087
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
12972
13088
|
resumeCandidate = current.data.candidate;
|
|
12973
13089
|
}
|
|
12974
|
-
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);
|
|
12975
13091
|
if (blocker)
|
|
12976
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
|
+
}
|
|
12977
13100
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12978
13101
|
taskId: task.data.id,
|
|
12979
|
-
|
|
13102
|
+
expectedRevision: input.expectedRevision,
|
|
12980
13103
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12981
13104
|
now: trustedNow(this.context),
|
|
12982
13105
|
workspace: (workspace, context) => success(context.revision, context.revision, workspace),
|
|
12983
13106
|
task: (current, context) => success(context.revision, null, {
|
|
12984
13107
|
...current,
|
|
12985
13108
|
status,
|
|
12986
|
-
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,
|
|
12987
13110
|
progress: current.progress,
|
|
12988
13111
|
pauseReason: status === "paused" ? input.reason ?? null : null
|
|
12989
13112
|
})
|
|
@@ -13021,12 +13144,13 @@ class WorkitCore {
|
|
|
13021
13144
|
return workspace;
|
|
13022
13145
|
if (!workspace.data)
|
|
13023
13146
|
return failure("not_found", "workspace not found");
|
|
13024
|
-
|
|
13025
|
-
|
|
13147
|
+
const revisionBlocker = this.activeWorkerBlocker(task.data, workspace.data);
|
|
13148
|
+
if (revisionBlocker)
|
|
13149
|
+
return revisionBlocker;
|
|
13026
13150
|
this.fillRevisions(input, task.data, workspace.data);
|
|
13027
13151
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
13028
13152
|
taskId: task.data.id,
|
|
13029
|
-
|
|
13153
|
+
expectedRevision: input.expectedRevision,
|
|
13030
13154
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
13031
13155
|
now: trustedNow(this.context),
|
|
13032
13156
|
workspace: (workspace, context) => success(context.revision, context.revision, workspace),
|
|
@@ -13082,7 +13206,7 @@ class WorkitCore {
|
|
|
13082
13206
|
return closure;
|
|
13083
13207
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
13084
13208
|
taskId: task.data.id,
|
|
13085
|
-
|
|
13209
|
+
expectedRevision: input.expectedRevision,
|
|
13086
13210
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
13087
13211
|
now: trustedNow(this.context),
|
|
13088
13212
|
workspace: (value, context) => success(context.revision, context.revision, value),
|
|
@@ -13173,7 +13297,7 @@ var cursorCapabilities = (availability = {}) => {
|
|
|
13173
13297
|
}
|
|
13174
13298
|
];
|
|
13175
13299
|
};
|
|
13176
|
-
var
|
|
13300
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13177
13301
|
var eventNames = new Set([
|
|
13178
13302
|
"sessionStart",
|
|
13179
13303
|
"preToolUse",
|
|
@@ -13184,7 +13308,7 @@ var eventNames = new Set([
|
|
|
13184
13308
|
]);
|
|
13185
13309
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
13186
13310
|
var parseCursorHookInput = (value) => {
|
|
13187
|
-
if (!
|
|
13311
|
+
if (!isRecord2(value) || !eventNames.has(value.hook_event_name))
|
|
13188
13312
|
return { ok: false, error: "hook_event_name is required" };
|
|
13189
13313
|
const roots = value.workspace_roots;
|
|
13190
13314
|
if (!Array.isArray(roots) || roots.length !== 1 || !roots.every(nonEmpty2))
|
|
@@ -13331,7 +13455,7 @@ var handleSubagentStop = () => ({});
|
|
|
13331
13455
|
var handleCursorHook = (raw) => {
|
|
13332
13456
|
const parsed = parseCursorHookInput(raw);
|
|
13333
13457
|
if (!parsed.ok) {
|
|
13334
|
-
return
|
|
13458
|
+
return isRecord2(raw) && raw.hook_event_name === "sessionStart" ? {} : deny(parsed.error);
|
|
13335
13459
|
}
|
|
13336
13460
|
const input = parsed.data;
|
|
13337
13461
|
const root = realpathSync5(input.workspace_roots[0]);
|
|
@@ -13376,7 +13500,7 @@ var runCursorHook = async () => {
|
|
|
13376
13500
|
const output = handleCursorHook(input);
|
|
13377
13501
|
process.stdout.write(`${JSON.stringify(output)}
|
|
13378
13502
|
`);
|
|
13379
|
-
const event =
|
|
13503
|
+
const event = isRecord2(input) ? input.hook_event_name : undefined;
|
|
13380
13504
|
if (output.permission === "deny" && ["preToolUse", "beforeShellExecution", "subagentStart"].includes(String(event)))
|
|
13381
13505
|
process.exitCode = 2;
|
|
13382
13506
|
} catch {
|