@brainervirus/workit-cursor 1.2.0 → 1.2.1
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 +114 -40
- package/dist/mcp-server.js +109 -36
- package/dist/workit-hook.js +109 -36
- package/package.json +3 -3
|
@@ -7572,7 +7572,7 @@ var SCHEMA_VERSION = 1, POLICY_VERSION = "1.0.0", OPERATION_FAMILIES, uuidPatter
|
|
|
7572
7572
|
}, rewriteRecordRefs = (value, schema, map) => {
|
|
7573
7573
|
const rewritten = rewriteNode(value, schema, map);
|
|
7574
7574
|
return rewritten.drop ? null : rewritten.value;
|
|
7575
|
-
}, provenanceSchema, entrySchema = (data) => object({ id, recordedAt: utc, provenance: provenanceSchema, data }).strict(), factSchema, signalSchema, signalSet, consequenceSchema, verificationSchema, assessmentSchema, constraintSchema, intentSchema, progressSchema, dimensionSchema, policySchema, requirementSchema, policyChangeSchema, candidateSchema, evidenceSchema, evidenceEvaluationSchema, decisionSchema, findingSchema, assignmentSchema, workerReportSchema, workerSchema, ownerSchema, closureSchema, actionProgressSchema, actionProgressListSchema, runtimeSchema, taskRecordSchema, workspaceRecordSchema, capabilitySchema, requirementEvaluationSchema, taskViewSchema, taskSummarySchema, exportBundleSchema, callerSchema, base, revisions, taskId, operation = (shape) => object({ ...base, ...shape }).strict(), taskOperations, policyOperations, evidenceOperations, findingOperations, decisionOperations, workerOperations, writerOperations, stateOperations, operationSchemas, compiledOperationSchemas, success = (revisionValue, workspaceRevisionValue, data) => ({
|
|
7575
|
+
}, provenanceSchema, entrySchema = (data) => object({ id, recordedAt: utc, provenance: provenanceSchema, data }).strict(), factSchema, signalSchema, signalSet, consequenceSchema, verificationSchema, assessmentSchema, constraintSchema, intentSchema, progressSchema, dimensionSchema, policySchema, requirementSchema, policyChangeSchema, candidateSchema, evidenceSchema, evidenceEvaluationSchema, decisionSchema, findingSchema, assignmentSchema, workerReportSchema, workerSchema, ownerSchema, closureSchema, actionProgressSchema, actionProgressListSchema, runtimeSchema, taskRecordSchema, workspaceRecordSchema, capabilitySchema, requirementEvaluationSchema, taskViewSchema, taskSummarySchema, taskListItemSchema, exportBundleSchema, callerSchema, base, revisions, taskId, operation = (shape) => object({ ...base, ...shape }).strict(), taskOperations, policyOperations, evidenceOperations, findingOperations, decisionOperations, workerOperations, writerOperations, stateOperations, operationSchemas, compiledOperationSchemas, success = (revisionValue, workspaceRevisionValue, data) => ({
|
|
7576
7576
|
ok: true,
|
|
7577
7577
|
schemaVersion: 1,
|
|
7578
7578
|
revision: revisionValue,
|
|
@@ -7923,6 +7923,7 @@ var init_task_contract = __esm(() => {
|
|
|
7923
7923
|
assignment: assignmentSchema,
|
|
7924
7924
|
state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
7925
7925
|
session: refSchema.nullable(),
|
|
7926
|
+
coordinator: refSchema.optional(),
|
|
7926
7927
|
report: workerReportSchema.nullable()
|
|
7927
7928
|
}).strict();
|
|
7928
7929
|
ownerSchema = object({
|
|
@@ -8034,6 +8035,7 @@ var init_task_contract = __esm(() => {
|
|
|
8034
8035
|
requirements: array(requirementEvaluationSchema),
|
|
8035
8036
|
writer: workspaceRecordSchema.shape.writer
|
|
8036
8037
|
}).strict();
|
|
8038
|
+
taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
|
|
8037
8039
|
exportBundleSchema = object({
|
|
8038
8040
|
schemaVersion: literal(1),
|
|
8039
8041
|
exportedAt: utc,
|
|
@@ -8054,11 +8056,15 @@ var init_task_contract = __esm(() => {
|
|
|
8054
8056
|
expectedWorkspaceRevision: revision.nullable().optional(),
|
|
8055
8057
|
intent: intentSchema
|
|
8056
8058
|
}),
|
|
8057
|
-
list: operation({
|
|
8059
|
+
list: operation({
|
|
8060
|
+
action: literal("list"),
|
|
8061
|
+
status: _enum(["open", "closed", "all"]).optional(),
|
|
8062
|
+
limit: number2().int().min(1).max(50).optional()
|
|
8063
|
+
}),
|
|
8058
8064
|
inspect: operation({
|
|
8059
8065
|
action: literal("inspect"),
|
|
8060
8066
|
...taskId,
|
|
8061
|
-
view: _enum(["summary", "full"])
|
|
8067
|
+
view: _enum(["summary", "full"]).optional()
|
|
8062
8068
|
}),
|
|
8063
8069
|
revise: operation({
|
|
8064
8070
|
action: literal("revise"),
|
|
@@ -8086,7 +8092,7 @@ var init_task_contract = __esm(() => {
|
|
|
8086
8092
|
...taskId,
|
|
8087
8093
|
...revisions,
|
|
8088
8094
|
expectedWorkspaceRevision: revision.optional(),
|
|
8089
|
-
authorityRefs: array(refSchema)
|
|
8095
|
+
authorityRefs: array(refSchema).optional()
|
|
8090
8096
|
}),
|
|
8091
8097
|
close: operation({
|
|
8092
8098
|
action: literal("close"),
|
|
@@ -9795,7 +9801,10 @@ var same = (left, right) => {
|
|
|
9795
9801
|
}, UNCERTAIN_WORKER_STATES, isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state), workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state), callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor }, validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
|
|
9796
9802
|
const code = char.charCodeAt(0);
|
|
9797
9803
|
return code < 32 || code === 127;
|
|
9798
|
-
}) && !value.split(/[\\/]/).includes(".."), callerValue = (value) => ({ host: value.host, actor: value.actor }), workerIdOf = (value) => value.workerId ?? null
|
|
9804
|
+
}) && !value.split(/[\\/]/).includes(".."), callerValue = (value) => ({ host: value.host, actor: value.actor }), workerIdOf = (value) => value.workerId ?? null, currentWriterOwnsTask = (task, workspace, caller) => {
|
|
9805
|
+
const owner = workspace.writer?.owner;
|
|
9806
|
+
return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
|
|
9807
|
+
};
|
|
9799
9808
|
var init_workers = __esm(() => {
|
|
9800
9809
|
init_task_contract();
|
|
9801
9810
|
UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
|
|
@@ -10100,9 +10109,11 @@ var AUTO_CLASSES, OPERATION_CLASS, operationAutoClass = (operation) => typeof op
|
|
|
10100
10109
|
}, verifyStandingApproval = (root, task, caller, binding) => {
|
|
10101
10110
|
if (task.status !== "active")
|
|
10102
10111
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
10103
|
-
const
|
|
10104
|
-
if (!
|
|
10105
|
-
return
|
|
10112
|
+
const workspace = new TaskStore(root).readWorkspace();
|
|
10113
|
+
if (!workspace.ok)
|
|
10114
|
+
return workspace;
|
|
10115
|
+
if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
|
|
10116
|
+
return failure("permission_denied", "standing approval requires the current writer session");
|
|
10106
10117
|
const standing = binding.standing;
|
|
10107
10118
|
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
10108
10119
|
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
@@ -10141,6 +10152,8 @@ var init_auto_approval = __esm(() => {
|
|
|
10141
10152
|
init_config();
|
|
10142
10153
|
init_branch();
|
|
10143
10154
|
init_task_contract();
|
|
10155
|
+
init_task_store();
|
|
10156
|
+
init_workers();
|
|
10144
10157
|
AUTO_CLASSES = ["branch", "commit", "push", "pr", "merge"];
|
|
10145
10158
|
OPERATION_CLASS = {
|
|
10146
10159
|
"git.branch_setup": "branch",
|
|
@@ -10289,7 +10302,7 @@ function settleAction(input) {
|
|
|
10289
10302
|
return failure("not_found", "decision not found");
|
|
10290
10303
|
if (!authorityDecisionMatches(authority, entry.data))
|
|
10291
10304
|
return failure("permission_denied", "native settlement authority does not match decision");
|
|
10292
|
-
if (!
|
|
10305
|
+
if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
10293
10306
|
return failure("permission_denied", "native settlement caller does not match approval");
|
|
10294
10307
|
if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
|
|
10295
10308
|
return failure("permission_denied", "decision binding is invalid");
|
|
@@ -10388,7 +10401,7 @@ function reconcileAction(input) {
|
|
|
10388
10401
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
10389
10402
|
if (!entry || entry.data.purpose !== "action")
|
|
10390
10403
|
return failure("not_found", "decision not found");
|
|
10391
|
-
if (!authorityDecisionMatches(authority, entry.data) || !
|
|
10404
|
+
if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
10392
10405
|
return failure("permission_denied", "native reconciliation caller does not match approval");
|
|
10393
10406
|
if (entry.data.response !== "approved" || entry.data.revoked !== null || entry.data.digest !== decisionDigest(entry.data) || entry.data.binding.taskId !== current.id || entry.data.binding.workspaceId !== current.workspaceId || entry.data.binding.workspaceId !== workspace.data.id)
|
|
10394
10407
|
return failure("permission_denied", "reconciliation decision binding is invalid");
|
|
@@ -10607,7 +10620,20 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10607
10620
|
}
|
|
10608
10621
|
}
|
|
10609
10622
|
return success(null, null, null);
|
|
10610
|
-
}, verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding), storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok),
|
|
10623
|
+
}, verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding), storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok), approvalAuthorityMatches = (store, task, approval, current) => {
|
|
10624
|
+
if (approval.kind !== "host_observed" || approval.receipts.length === 0)
|
|
10625
|
+
return false;
|
|
10626
|
+
if (provenanceMatches(approval, current))
|
|
10627
|
+
return true;
|
|
10628
|
+
const session = current.kind === "host_observed" ? current.session : null;
|
|
10629
|
+
if (!session || session.kind !== "host")
|
|
10630
|
+
return false;
|
|
10631
|
+
const workspace = store.readWorkspace();
|
|
10632
|
+
return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
|
|
10633
|
+
host: session.host,
|
|
10634
|
+
actor: session.handle
|
|
10635
|
+
});
|
|
10636
|
+
}, validateAction = (store, task, workspaceId, input, authority) => {
|
|
10611
10637
|
if (task.status !== "active")
|
|
10612
10638
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
10613
10639
|
if (!refSchema.safeParse(input.actionRef).success)
|
|
@@ -10632,7 +10658,7 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10632
10658
|
return failure("permission_denied", "native reservation authority does not match decision");
|
|
10633
10659
|
if (decision.response !== "approved")
|
|
10634
10660
|
return failure("permission_denied", "decision was rejected");
|
|
10635
|
-
if (
|
|
10661
|
+
if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
|
|
10636
10662
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
10637
10663
|
const receipts = entry.provenance.receipts;
|
|
10638
10664
|
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
@@ -10673,6 +10699,7 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10673
10699
|
var init_authority = __esm(() => {
|
|
10674
10700
|
init_auto_approval();
|
|
10675
10701
|
init_task_contract();
|
|
10702
|
+
init_workers();
|
|
10676
10703
|
verifiedAuthorities = new WeakMap;
|
|
10677
10704
|
bindingCovers = scopeCovers;
|
|
10678
10705
|
verifyDecisionContent = verifyContentRefs;
|
|
@@ -11124,7 +11151,7 @@ var digestBytes3 = (value) => createHash5("sha256").update(value).digest("hex"),
|
|
|
11124
11151
|
beforeCandidateId: evidence.beforeCandidateId ?? fallback,
|
|
11125
11152
|
candidateId: evidence.candidateId ?? fallback
|
|
11126
11153
|
};
|
|
11127
|
-
}, findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId
|
|
11154
|
+
}, findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null), 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"))), 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 })), evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
11128
11155
|
if (kind !== "passed")
|
|
11129
11156
|
return false;
|
|
11130
11157
|
if (dimension === "testing" || dimension === "verification")
|
|
@@ -11367,11 +11394,16 @@ var init_route_intent = __esm(() => {
|
|
|
11367
11394
|
var init_runtime_identity = () => {};
|
|
11368
11395
|
|
|
11369
11396
|
// packages/workit-core/src/core/external-action.ts
|
|
11370
|
-
var externalActionSchema;
|
|
11397
|
+
var chainStepSchema, externalActionSchema;
|
|
11371
11398
|
var init_external_action = __esm(() => {
|
|
11372
11399
|
init_task_contract();
|
|
11373
11400
|
init_authority();
|
|
11374
11401
|
init_zod();
|
|
11402
|
+
chainStepSchema = union([
|
|
11403
|
+
string2().min(1),
|
|
11404
|
+
object({ branch: string2().min(1) }).strict(),
|
|
11405
|
+
object({ pr: literal(true) }).strict()
|
|
11406
|
+
]);
|
|
11375
11407
|
externalActionSchema = discriminatedUnion("operation", [
|
|
11376
11408
|
object({
|
|
11377
11409
|
operation: literal("git.branch_setup"),
|
|
@@ -11386,7 +11418,7 @@ var init_external_action = __esm(() => {
|
|
|
11386
11418
|
operation: literal("git.commit"),
|
|
11387
11419
|
payload: object({
|
|
11388
11420
|
message: string2().min(1).optional(),
|
|
11389
|
-
plan_steps: array(
|
|
11421
|
+
plan_steps: array(chainStepSchema).min(1).max(32).optional(),
|
|
11390
11422
|
plan_branch: string2().min(1).optional()
|
|
11391
11423
|
}).strict()
|
|
11392
11424
|
}).strict(),
|
|
@@ -11990,14 +12022,30 @@ class WorkitCore {
|
|
|
11990
12022
|
const listed = this.store.listTasks();
|
|
11991
12023
|
if (!listed.ok)
|
|
11992
12024
|
return listed;
|
|
11993
|
-
const
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
|
|
11997
|
-
|
|
11998
|
-
|
|
11999
|
-
|
|
12000
|
-
|
|
12025
|
+
const status = input.status ?? "open";
|
|
12026
|
+
const limit = input.limit ?? 20;
|
|
12027
|
+
const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
|
|
12028
|
+
if (selected.length === 0)
|
|
12029
|
+
return success(null, null, []);
|
|
12030
|
+
const workspace = this.store.readWorkspace();
|
|
12031
|
+
if (!workspace.ok)
|
|
12032
|
+
return workspace;
|
|
12033
|
+
if (!workspace.data)
|
|
12034
|
+
return failure("not_found", "workspace not found");
|
|
12035
|
+
const tasks = selected.map((task) => ({
|
|
12036
|
+
id: task.id,
|
|
12037
|
+
revision: task.revision,
|
|
12038
|
+
workspaceRevision: workspace.data.revision,
|
|
12039
|
+
createdAt: task.createdAt,
|
|
12040
|
+
updatedAt: task.updatedAt,
|
|
12041
|
+
runtime: task.runtime ?? null,
|
|
12042
|
+
objective: task.intent.data.objective,
|
|
12043
|
+
status: task.status,
|
|
12044
|
+
closure: task.closure,
|
|
12045
|
+
progress: task.progress,
|
|
12046
|
+
writer: task.status === "closed" ? null : workspace.data.writer
|
|
12047
|
+
}));
|
|
12048
|
+
return success(null, null, tasks);
|
|
12001
12049
|
}
|
|
12002
12050
|
case "inspect": {
|
|
12003
12051
|
const task = this.store.readTask(input.taskId);
|
|
@@ -12006,7 +12054,7 @@ class WorkitCore {
|
|
|
12006
12054
|
const helper = this.helperTaskGuard(task.data, true);
|
|
12007
12055
|
if (!helper.ok)
|
|
12008
12056
|
return helper;
|
|
12009
|
-
if (input.view === "summary")
|
|
12057
|
+
if ((input.view ?? "summary") === "summary")
|
|
12010
12058
|
return this.summary(task.data);
|
|
12011
12059
|
return this.view(task.data);
|
|
12012
12060
|
}
|
|
@@ -12350,7 +12398,7 @@ class WorkitCore {
|
|
|
12350
12398
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
12351
12399
|
const verified = evidence.some((entry) => {
|
|
12352
12400
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
12353
|
-
return findingVerificationPasses(finding.data.candidateId, {
|
|
12401
|
+
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
12354
12402
|
kind: entry.data.kind,
|
|
12355
12403
|
candidateId: entry.data.candidateId,
|
|
12356
12404
|
status: evaluation?.status ?? "missing"
|
|
@@ -12430,7 +12478,18 @@ class WorkitCore {
|
|
|
12430
12478
|
if (task.data.status !== "active")
|
|
12431
12479
|
return failure("invalid_transition", "paused or closed tasks cannot assign workers");
|
|
12432
12480
|
if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
|
|
12433
|
-
return failure("permission_denied", "worker assignment is outside task scope"
|
|
12481
|
+
return failure("permission_denied", "worker assignment is outside task scope", {
|
|
12482
|
+
fields: [
|
|
12483
|
+
{
|
|
12484
|
+
path: "assignment.scope",
|
|
12485
|
+
reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
12486
|
+
},
|
|
12487
|
+
{
|
|
12488
|
+
path: "task.intent.scope",
|
|
12489
|
+
reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
12490
|
+
}
|
|
12491
|
+
]
|
|
12492
|
+
});
|
|
12434
12493
|
if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
|
|
12435
12494
|
return failure("invalid_input", "worker assignment references an unknown decision");
|
|
12436
12495
|
if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
|
|
@@ -12637,7 +12696,15 @@ class WorkitCore {
|
|
|
12637
12696
|
...item,
|
|
12638
12697
|
recordedAt: mutation.now,
|
|
12639
12698
|
provenance: attested.data,
|
|
12640
|
-
data: {
|
|
12699
|
+
data: {
|
|
12700
|
+
...item.data,
|
|
12701
|
+
state: "dispatching",
|
|
12702
|
+
coordinator: {
|
|
12703
|
+
kind: "host",
|
|
12704
|
+
host: this.context.caller.host,
|
|
12705
|
+
handle: this.context.caller.actor
|
|
12706
|
+
}
|
|
12707
|
+
}
|
|
12641
12708
|
} : item)
|
|
12642
12709
|
});
|
|
12643
12710
|
}
|
|
@@ -12713,6 +12780,7 @@ class WorkitCore {
|
|
|
12713
12780
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12714
12781
|
state: "stopped",
|
|
12715
12782
|
session: null,
|
|
12783
|
+
report: null,
|
|
12716
12784
|
provenance: attested.data,
|
|
12717
12785
|
owner: this.authorityOwner,
|
|
12718
12786
|
store: this.store,
|
|
@@ -13020,10 +13088,12 @@ class WorkitCore {
|
|
|
13020
13088
|
return workspace;
|
|
13021
13089
|
if (!workspace.data)
|
|
13022
13090
|
return failure("not_found", "workspace not found");
|
|
13023
|
-
const
|
|
13091
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
13092
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
13024
13093
|
if (!current.ok)
|
|
13025
13094
|
return current;
|
|
13026
|
-
const
|
|
13095
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
13096
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
13027
13097
|
return success(task.revision, workspace.data.revision, {
|
|
13028
13098
|
id: task.id,
|
|
13029
13099
|
revision: task.revision,
|
|
@@ -13037,7 +13107,7 @@ class WorkitCore {
|
|
|
13037
13107
|
progress: task.progress,
|
|
13038
13108
|
policy: task.policy,
|
|
13039
13109
|
requirements,
|
|
13040
|
-
writer:
|
|
13110
|
+
writer: evaluationWorkspace.writer
|
|
13041
13111
|
});
|
|
13042
13112
|
}
|
|
13043
13113
|
view(task) {
|
|
@@ -13046,13 +13116,15 @@ class WorkitCore {
|
|
|
13046
13116
|
return workspace;
|
|
13047
13117
|
if (!workspace.data)
|
|
13048
13118
|
return failure("not_found", "workspace not found");
|
|
13049
|
-
const
|
|
13119
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
13120
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
13050
13121
|
if (!current.ok)
|
|
13051
13122
|
return current;
|
|
13052
|
-
const
|
|
13123
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
13124
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
13053
13125
|
return success(task.revision, workspace.data.revision, {
|
|
13054
13126
|
task,
|
|
13055
|
-
workspace:
|
|
13127
|
+
workspace: evaluationWorkspace,
|
|
13056
13128
|
capabilities: this.context.capabilities,
|
|
13057
13129
|
requirements,
|
|
13058
13130
|
evidence: evaluateEvidence(task, current.data)
|
|
@@ -13143,8 +13215,6 @@ class WorkitCore {
|
|
|
13143
13215
|
return helper;
|
|
13144
13216
|
if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
|
|
13145
13217
|
return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
|
|
13146
|
-
if (status === "active" && input.authorityRefs.length === 0)
|
|
13147
|
-
return failure("permission_denied", "resuming a task requires authority references");
|
|
13148
13218
|
const workspace = this.store.readWorkspace();
|
|
13149
13219
|
if (!workspace.ok)
|
|
13150
13220
|
return workspace;
|
|
@@ -13174,7 +13244,7 @@ class WorkitCore {
|
|
|
13174
13244
|
return failure("needs_input", "imported task requires fresh destination evidence");
|
|
13175
13245
|
if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
|
|
13176
13246
|
return failure("recovery_required", "imported task requires resume reconciliation");
|
|
13177
|
-
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
|
|
13247
|
+
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
|
|
13178
13248
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
13179
13249
|
resumeCandidate = current.data.candidate;
|
|
13180
13250
|
}
|
|
@@ -13304,7 +13374,7 @@ class WorkitCore {
|
|
|
13304
13374
|
task: (value, context) => success(context.revision, null, {
|
|
13305
13375
|
...value,
|
|
13306
13376
|
status: "closed",
|
|
13307
|
-
candidates:
|
|
13377
|
+
candidates: taskForView.candidates,
|
|
13308
13378
|
closure: {
|
|
13309
13379
|
outcome: closure.data.outcome,
|
|
13310
13380
|
at: context.now,
|
|
@@ -13381,6 +13451,7 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
13381
13451
|
expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
|
|
13382
13452
|
state: expected.state,
|
|
13383
13453
|
session: expected.session,
|
|
13454
|
+
report: expected.report ?? null,
|
|
13384
13455
|
provenance: result.data,
|
|
13385
13456
|
...binding
|
|
13386
13457
|
});
|
|
@@ -13390,7 +13461,7 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
13390
13461
|
verifiedWorkers.delete(token);
|
|
13391
13462
|
if (!value)
|
|
13392
13463
|
return null;
|
|
13393
|
-
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session))
|
|
13464
|
+
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session) || !sameValue(value.report, expected.report ?? null))
|
|
13394
13465
|
return null;
|
|
13395
13466
|
return value;
|
|
13396
13467
|
}, applyWorkerLifecycle = (input) => {
|
|
@@ -13430,6 +13501,8 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
13430
13501
|
return failure("invalid_transition", "worker already has an observed session");
|
|
13431
13502
|
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
13432
13503
|
return failure("invalid_transition", "worker launch was not claimed");
|
|
13504
|
+
if (input.report && input.state !== "stopped")
|
|
13505
|
+
return failure("invalid_input", "worker reports require a stopped worker");
|
|
13433
13506
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
13434
13507
|
return failure("invalid_input", "running worker observations require a session");
|
|
13435
13508
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -13446,12 +13519,13 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
13446
13519
|
data: {
|
|
13447
13520
|
...entry.data,
|
|
13448
13521
|
state: terminalCancel ? "cancelling" : input.state,
|
|
13449
|
-
session: input.session
|
|
13522
|
+
session: input.session,
|
|
13523
|
+
report: entry.data.report ?? input.report ?? null
|
|
13450
13524
|
}
|
|
13451
13525
|
};
|
|
13452
13526
|
const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
13453
13527
|
const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
13454
|
-
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
|
|
13528
|
+
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
|
|
13455
13529
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
13456
13530
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
13457
13531
|
taskId: task.data.id,
|
package/dist/mcp-server.js
CHANGED
|
@@ -20404,6 +20404,7 @@ var workerSchema = object4({
|
|
|
20404
20404
|
assignment: assignmentSchema,
|
|
20405
20405
|
state: _enum2(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
20406
20406
|
session: refSchema.nullable(),
|
|
20407
|
+
coordinator: refSchema.optional(),
|
|
20407
20408
|
report: workerReportSchema.nullable()
|
|
20408
20409
|
}).strict();
|
|
20409
20410
|
var ownerSchema = object4({
|
|
@@ -20515,6 +20516,7 @@ var taskSummarySchema = object4({
|
|
|
20515
20516
|
requirements: array3(requirementEvaluationSchema),
|
|
20516
20517
|
writer: workspaceRecordSchema.shape.writer
|
|
20517
20518
|
}).strict();
|
|
20519
|
+
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
|
|
20518
20520
|
var exportBundleSchema = object4({
|
|
20519
20521
|
schemaVersion: literal3(1),
|
|
20520
20522
|
exportedAt: utc,
|
|
@@ -20536,11 +20538,15 @@ var taskOperations = {
|
|
|
20536
20538
|
expectedWorkspaceRevision: revision.nullable().optional(),
|
|
20537
20539
|
intent: intentSchema
|
|
20538
20540
|
}),
|
|
20539
|
-
list: operation({
|
|
20541
|
+
list: operation({
|
|
20542
|
+
action: literal3("list"),
|
|
20543
|
+
status: _enum2(["open", "closed", "all"]).optional(),
|
|
20544
|
+
limit: number5().int().min(1).max(50).optional()
|
|
20545
|
+
}),
|
|
20540
20546
|
inspect: operation({
|
|
20541
20547
|
action: literal3("inspect"),
|
|
20542
20548
|
...taskId,
|
|
20543
|
-
view: _enum2(["summary", "full"])
|
|
20549
|
+
view: _enum2(["summary", "full"]).optional()
|
|
20544
20550
|
}),
|
|
20545
20551
|
revise: operation({
|
|
20546
20552
|
action: literal3("revise"),
|
|
@@ -20568,7 +20574,7 @@ var taskOperations = {
|
|
|
20568
20574
|
...taskId,
|
|
20569
20575
|
...revisions,
|
|
20570
20576
|
expectedWorkspaceRevision: revision.optional(),
|
|
20571
|
-
authorityRefs: array3(refSchema)
|
|
20577
|
+
authorityRefs: array3(refSchema).optional()
|
|
20572
20578
|
}),
|
|
20573
20579
|
close: operation({
|
|
20574
20580
|
action: literal3("close"),
|
|
@@ -22401,6 +22407,10 @@ var validPath = (value) => typeof value === "string" && value.length > 0 && ![..
|
|
|
22401
22407
|
}) && !value.split(/[\\/]/).includes("..");
|
|
22402
22408
|
var callerValue = (value) => ({ host: value.host, actor: value.actor });
|
|
22403
22409
|
var workerIdOf = (value) => value.workerId ?? null;
|
|
22410
|
+
var currentWriterOwnsTask = (task, workspace, caller) => {
|
|
22411
|
+
const owner = workspace.writer?.owner;
|
|
22412
|
+
return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
|
|
22413
|
+
};
|
|
22404
22414
|
function assertProductWriteAllowed(input) {
|
|
22405
22415
|
if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
|
|
22406
22416
|
return failure("invalid_input", "invalid product write path");
|
|
@@ -23117,9 +23127,11 @@ var standingReceiptFor = (root, workspace, cls, dir) => {
|
|
|
23117
23127
|
var verifyStandingApproval = (root, task, caller, binding) => {
|
|
23118
23128
|
if (task.status !== "active")
|
|
23119
23129
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
23120
|
-
const
|
|
23121
|
-
if (!
|
|
23122
|
-
return
|
|
23130
|
+
const workspace = new TaskStore(root).readWorkspace();
|
|
23131
|
+
if (!workspace.ok)
|
|
23132
|
+
return workspace;
|
|
23133
|
+
if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
|
|
23134
|
+
return failure("permission_denied", "standing approval requires the current writer session");
|
|
23123
23135
|
const standing = binding.standing;
|
|
23124
23136
|
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
23125
23137
|
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
@@ -23356,6 +23368,20 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
23356
23368
|
};
|
|
23357
23369
|
var verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding);
|
|
23358
23370
|
var storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok);
|
|
23371
|
+
var approvalAuthorityMatches = (store, task, approval, current) => {
|
|
23372
|
+
if (approval.kind !== "host_observed" || approval.receipts.length === 0)
|
|
23373
|
+
return false;
|
|
23374
|
+
if (provenanceMatches(approval, current))
|
|
23375
|
+
return true;
|
|
23376
|
+
const session = current.kind === "host_observed" ? current.session : null;
|
|
23377
|
+
if (!session || session.kind !== "host")
|
|
23378
|
+
return false;
|
|
23379
|
+
const workspace = store.readWorkspace();
|
|
23380
|
+
return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
|
|
23381
|
+
host: session.host,
|
|
23382
|
+
actor: session.handle
|
|
23383
|
+
});
|
|
23384
|
+
};
|
|
23359
23385
|
var validateAction = (store, task, workspaceId, input, authority) => {
|
|
23360
23386
|
if (task.status !== "active")
|
|
23361
23387
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
@@ -23381,7 +23407,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
23381
23407
|
return failure("permission_denied", "native reservation authority does not match decision");
|
|
23382
23408
|
if (decision.response !== "approved")
|
|
23383
23409
|
return failure("permission_denied", "decision was rejected");
|
|
23384
|
-
if (
|
|
23410
|
+
if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
|
|
23385
23411
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
23386
23412
|
const receipts = entry.provenance.receipts;
|
|
23387
23413
|
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
@@ -23548,7 +23574,7 @@ function settleAction(input) {
|
|
|
23548
23574
|
return failure("not_found", "decision not found");
|
|
23549
23575
|
if (!authorityDecisionMatches(authority, entry.data))
|
|
23550
23576
|
return failure("permission_denied", "native settlement authority does not match decision");
|
|
23551
|
-
if (!
|
|
23577
|
+
if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
23552
23578
|
return failure("permission_denied", "native settlement caller does not match approval");
|
|
23553
23579
|
if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
|
|
23554
23580
|
return failure("permission_denied", "decision binding is invalid");
|
|
@@ -23647,7 +23673,7 @@ function reconcileAction(input) {
|
|
|
23647
23673
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
23648
23674
|
if (!entry || entry.data.purpose !== "action")
|
|
23649
23675
|
return failure("not_found", "decision not found");
|
|
23650
|
-
if (!authorityDecisionMatches(authority, entry.data) || !
|
|
23676
|
+
if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
23651
23677
|
return failure("permission_denied", "native reconciliation caller does not match approval");
|
|
23652
23678
|
if (entry.data.response !== "approved" || entry.data.revoked !== null || entry.data.digest !== decisionDigest(entry.data) || entry.data.binding.taskId !== current.id || entry.data.binding.workspaceId !== current.workspaceId || entry.data.binding.workspaceId !== workspace.data.id)
|
|
23653
23679
|
return failure("permission_denied", "reconciliation decision binding is invalid");
|
|
@@ -23984,7 +24010,7 @@ var resolveBinding = (evidence, pin, currentCandidateId) => {
|
|
|
23984
24010
|
candidateId: evidence.candidateId ?? fallback
|
|
23985
24011
|
};
|
|
23986
24012
|
};
|
|
23987
|
-
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId
|
|
24013
|
+
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null);
|
|
23988
24014
|
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")));
|
|
23989
24015
|
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 }));
|
|
23990
24016
|
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
@@ -24219,6 +24245,11 @@ var exportDigest = (bundle) => sha256({
|
|
|
24219
24245
|
task: bundle.task
|
|
24220
24246
|
});
|
|
24221
24247
|
// packages/workit-core/src/core/external-action.ts
|
|
24248
|
+
var chainStepSchema = union3([
|
|
24249
|
+
string5().min(1),
|
|
24250
|
+
object4({ branch: string5().min(1) }).strict(),
|
|
24251
|
+
object4({ pr: literal3(true) }).strict()
|
|
24252
|
+
]);
|
|
24222
24253
|
var externalActionSchema = discriminatedUnion2("operation", [
|
|
24223
24254
|
object4({
|
|
24224
24255
|
operation: literal3("git.branch_setup"),
|
|
@@ -24233,7 +24264,7 @@ var externalActionSchema = discriminatedUnion2("operation", [
|
|
|
24233
24264
|
operation: literal3("git.commit"),
|
|
24234
24265
|
payload: object4({
|
|
24235
24266
|
message: string5().min(1).optional(),
|
|
24236
|
-
plan_steps: array3(
|
|
24267
|
+
plan_steps: array3(chainStepSchema).min(1).max(32).optional(),
|
|
24237
24268
|
plan_branch: string5().min(1).optional()
|
|
24238
24269
|
}).strict()
|
|
24239
24270
|
}).strict(),
|
|
@@ -24719,6 +24750,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
|
|
|
24719
24750
|
expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
|
|
24720
24751
|
state: expected.state,
|
|
24721
24752
|
session: expected.session,
|
|
24753
|
+
report: expected.report ?? null,
|
|
24722
24754
|
provenance: result.data,
|
|
24723
24755
|
...binding
|
|
24724
24756
|
});
|
|
@@ -24729,7 +24761,7 @@ var takeWorker = (token, binding, expected) => {
|
|
|
24729
24761
|
verifiedWorkers.delete(token);
|
|
24730
24762
|
if (!value)
|
|
24731
24763
|
return null;
|
|
24732
|
-
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session))
|
|
24764
|
+
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session) || !sameValue(value.report, expected.report ?? null))
|
|
24733
24765
|
return null;
|
|
24734
24766
|
return value;
|
|
24735
24767
|
};
|
|
@@ -24770,6 +24802,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
24770
24802
|
return failure("invalid_transition", "worker already has an observed session");
|
|
24771
24803
|
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
24772
24804
|
return failure("invalid_transition", "worker launch was not claimed");
|
|
24805
|
+
if (input.report && input.state !== "stopped")
|
|
24806
|
+
return failure("invalid_input", "worker reports require a stopped worker");
|
|
24773
24807
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
24774
24808
|
return failure("invalid_input", "running worker observations require a session");
|
|
24775
24809
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -24786,12 +24820,13 @@ var applyWorkerLifecycle = (input) => {
|
|
|
24786
24820
|
data: {
|
|
24787
24821
|
...entry.data,
|
|
24788
24822
|
state: terminalCancel ? "cancelling" : input.state,
|
|
24789
|
-
session: input.session
|
|
24823
|
+
session: input.session,
|
|
24824
|
+
report: entry.data.report ?? input.report ?? null
|
|
24790
24825
|
}
|
|
24791
24826
|
};
|
|
24792
24827
|
const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
24793
24828
|
const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
24794
|
-
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
|
|
24829
|
+
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
|
|
24795
24830
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
24796
24831
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
24797
24832
|
taskId: task.data.id,
|
|
@@ -25209,14 +25244,30 @@ class WorkitCore {
|
|
|
25209
25244
|
const listed = this.store.listTasks();
|
|
25210
25245
|
if (!listed.ok)
|
|
25211
25246
|
return listed;
|
|
25212
|
-
const
|
|
25213
|
-
|
|
25214
|
-
|
|
25215
|
-
|
|
25216
|
-
|
|
25217
|
-
|
|
25218
|
-
|
|
25219
|
-
|
|
25247
|
+
const status = input.status ?? "open";
|
|
25248
|
+
const limit = input.limit ?? 20;
|
|
25249
|
+
const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
|
|
25250
|
+
if (selected.length === 0)
|
|
25251
|
+
return success(null, null, []);
|
|
25252
|
+
const workspace = this.store.readWorkspace();
|
|
25253
|
+
if (!workspace.ok)
|
|
25254
|
+
return workspace;
|
|
25255
|
+
if (!workspace.data)
|
|
25256
|
+
return failure("not_found", "workspace not found");
|
|
25257
|
+
const tasks = selected.map((task) => ({
|
|
25258
|
+
id: task.id,
|
|
25259
|
+
revision: task.revision,
|
|
25260
|
+
workspaceRevision: workspace.data.revision,
|
|
25261
|
+
createdAt: task.createdAt,
|
|
25262
|
+
updatedAt: task.updatedAt,
|
|
25263
|
+
runtime: task.runtime ?? null,
|
|
25264
|
+
objective: task.intent.data.objective,
|
|
25265
|
+
status: task.status,
|
|
25266
|
+
closure: task.closure,
|
|
25267
|
+
progress: task.progress,
|
|
25268
|
+
writer: task.status === "closed" ? null : workspace.data.writer
|
|
25269
|
+
}));
|
|
25270
|
+
return success(null, null, tasks);
|
|
25220
25271
|
}
|
|
25221
25272
|
case "inspect": {
|
|
25222
25273
|
const task = this.store.readTask(input.taskId);
|
|
@@ -25225,7 +25276,7 @@ class WorkitCore {
|
|
|
25225
25276
|
const helper = this.helperTaskGuard(task.data, true);
|
|
25226
25277
|
if (!helper.ok)
|
|
25227
25278
|
return helper;
|
|
25228
|
-
if (input.view === "summary")
|
|
25279
|
+
if ((input.view ?? "summary") === "summary")
|
|
25229
25280
|
return this.summary(task.data);
|
|
25230
25281
|
return this.view(task.data);
|
|
25231
25282
|
}
|
|
@@ -25569,7 +25620,7 @@ class WorkitCore {
|
|
|
25569
25620
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
25570
25621
|
const verified = evidence.some((entry) => {
|
|
25571
25622
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
25572
|
-
return findingVerificationPasses(finding.data.candidateId, {
|
|
25623
|
+
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
25573
25624
|
kind: entry.data.kind,
|
|
25574
25625
|
candidateId: entry.data.candidateId,
|
|
25575
25626
|
status: evaluation?.status ?? "missing"
|
|
@@ -25649,7 +25700,18 @@ class WorkitCore {
|
|
|
25649
25700
|
if (task.data.status !== "active")
|
|
25650
25701
|
return failure("invalid_transition", "paused or closed tasks cannot assign workers");
|
|
25651
25702
|
if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
|
|
25652
|
-
return failure("permission_denied", "worker assignment is outside task scope"
|
|
25703
|
+
return failure("permission_denied", "worker assignment is outside task scope", {
|
|
25704
|
+
fields: [
|
|
25705
|
+
{
|
|
25706
|
+
path: "assignment.scope",
|
|
25707
|
+
reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
25708
|
+
},
|
|
25709
|
+
{
|
|
25710
|
+
path: "task.intent.scope",
|
|
25711
|
+
reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
25712
|
+
}
|
|
25713
|
+
]
|
|
25714
|
+
});
|
|
25653
25715
|
if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
|
|
25654
25716
|
return failure("invalid_input", "worker assignment references an unknown decision");
|
|
25655
25717
|
if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
|
|
@@ -25856,7 +25918,15 @@ class WorkitCore {
|
|
|
25856
25918
|
...item,
|
|
25857
25919
|
recordedAt: mutation.now,
|
|
25858
25920
|
provenance: attested.data,
|
|
25859
|
-
data: {
|
|
25921
|
+
data: {
|
|
25922
|
+
...item.data,
|
|
25923
|
+
state: "dispatching",
|
|
25924
|
+
coordinator: {
|
|
25925
|
+
kind: "host",
|
|
25926
|
+
host: this.context.caller.host,
|
|
25927
|
+
handle: this.context.caller.actor
|
|
25928
|
+
}
|
|
25929
|
+
}
|
|
25860
25930
|
} : item)
|
|
25861
25931
|
});
|
|
25862
25932
|
}
|
|
@@ -25932,6 +26002,7 @@ class WorkitCore {
|
|
|
25932
26002
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
25933
26003
|
state: "stopped",
|
|
25934
26004
|
session: null,
|
|
26005
|
+
report: null,
|
|
25935
26006
|
provenance: attested.data,
|
|
25936
26007
|
owner: this.authorityOwner,
|
|
25937
26008
|
store: this.store,
|
|
@@ -26239,10 +26310,12 @@ class WorkitCore {
|
|
|
26239
26310
|
return workspace;
|
|
26240
26311
|
if (!workspace.data)
|
|
26241
26312
|
return failure("not_found", "workspace not found");
|
|
26242
|
-
const
|
|
26313
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
26314
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
26243
26315
|
if (!current.ok)
|
|
26244
26316
|
return current;
|
|
26245
|
-
const
|
|
26317
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
26318
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
26246
26319
|
return success(task.revision, workspace.data.revision, {
|
|
26247
26320
|
id: task.id,
|
|
26248
26321
|
revision: task.revision,
|
|
@@ -26256,7 +26329,7 @@ class WorkitCore {
|
|
|
26256
26329
|
progress: task.progress,
|
|
26257
26330
|
policy: task.policy,
|
|
26258
26331
|
requirements,
|
|
26259
|
-
writer:
|
|
26332
|
+
writer: evaluationWorkspace.writer
|
|
26260
26333
|
});
|
|
26261
26334
|
}
|
|
26262
26335
|
view(task) {
|
|
@@ -26265,13 +26338,15 @@ class WorkitCore {
|
|
|
26265
26338
|
return workspace;
|
|
26266
26339
|
if (!workspace.data)
|
|
26267
26340
|
return failure("not_found", "workspace not found");
|
|
26268
|
-
const
|
|
26341
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
26342
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
26269
26343
|
if (!current.ok)
|
|
26270
26344
|
return current;
|
|
26271
|
-
const
|
|
26345
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
26346
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
26272
26347
|
return success(task.revision, workspace.data.revision, {
|
|
26273
26348
|
task,
|
|
26274
|
-
workspace:
|
|
26349
|
+
workspace: evaluationWorkspace,
|
|
26275
26350
|
capabilities: this.context.capabilities,
|
|
26276
26351
|
requirements,
|
|
26277
26352
|
evidence: evaluateEvidence(task, current.data)
|
|
@@ -26362,8 +26437,6 @@ class WorkitCore {
|
|
|
26362
26437
|
return helper;
|
|
26363
26438
|
if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
|
|
26364
26439
|
return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
|
|
26365
|
-
if (status === "active" && input.authorityRefs.length === 0)
|
|
26366
|
-
return failure("permission_denied", "resuming a task requires authority references");
|
|
26367
26440
|
const workspace = this.store.readWorkspace();
|
|
26368
26441
|
if (!workspace.ok)
|
|
26369
26442
|
return workspace;
|
|
@@ -26393,7 +26466,7 @@ class WorkitCore {
|
|
|
26393
26466
|
return failure("needs_input", "imported task requires fresh destination evidence");
|
|
26394
26467
|
if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
|
|
26395
26468
|
return failure("recovery_required", "imported task requires resume reconciliation");
|
|
26396
|
-
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
|
|
26469
|
+
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
|
|
26397
26470
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
26398
26471
|
resumeCandidate = current.data.candidate;
|
|
26399
26472
|
}
|
|
@@ -26523,7 +26596,7 @@ class WorkitCore {
|
|
|
26523
26596
|
task: (value, context) => success(context.revision, null, {
|
|
26524
26597
|
...value,
|
|
26525
26598
|
status: "closed",
|
|
26526
|
-
candidates:
|
|
26599
|
+
candidates: taskForView.candidates,
|
|
26527
26600
|
closure: {
|
|
26528
26601
|
outcome: closure.data.outcome,
|
|
26529
26602
|
at: context.now,
|
package/dist/workit-hook.js
CHANGED
|
@@ -7705,6 +7705,7 @@ var workerSchema = object({
|
|
|
7705
7705
|
assignment: assignmentSchema,
|
|
7706
7706
|
state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
7707
7707
|
session: refSchema.nullable(),
|
|
7708
|
+
coordinator: refSchema.optional(),
|
|
7708
7709
|
report: workerReportSchema.nullable()
|
|
7709
7710
|
}).strict();
|
|
7710
7711
|
var ownerSchema = object({
|
|
@@ -7816,6 +7817,7 @@ var taskSummarySchema = object({
|
|
|
7816
7817
|
requirements: array(requirementEvaluationSchema),
|
|
7817
7818
|
writer: workspaceRecordSchema.shape.writer
|
|
7818
7819
|
}).strict();
|
|
7820
|
+
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
|
|
7819
7821
|
var exportBundleSchema = object({
|
|
7820
7822
|
schemaVersion: literal(1),
|
|
7821
7823
|
exportedAt: utc,
|
|
@@ -7837,11 +7839,15 @@ var taskOperations = {
|
|
|
7837
7839
|
expectedWorkspaceRevision: revision.nullable().optional(),
|
|
7838
7840
|
intent: intentSchema
|
|
7839
7841
|
}),
|
|
7840
|
-
list: operation({
|
|
7842
|
+
list: operation({
|
|
7843
|
+
action: literal("list"),
|
|
7844
|
+
status: _enum(["open", "closed", "all"]).optional(),
|
|
7845
|
+
limit: number2().int().min(1).max(50).optional()
|
|
7846
|
+
}),
|
|
7841
7847
|
inspect: operation({
|
|
7842
7848
|
action: literal("inspect"),
|
|
7843
7849
|
...taskId,
|
|
7844
|
-
view: _enum(["summary", "full"])
|
|
7850
|
+
view: _enum(["summary", "full"]).optional()
|
|
7845
7851
|
}),
|
|
7846
7852
|
revise: operation({
|
|
7847
7853
|
action: literal("revise"),
|
|
@@ -7869,7 +7875,7 @@ var taskOperations = {
|
|
|
7869
7875
|
...taskId,
|
|
7870
7876
|
...revisions,
|
|
7871
7877
|
expectedWorkspaceRevision: revision.optional(),
|
|
7872
|
-
authorityRefs: array(refSchema)
|
|
7878
|
+
authorityRefs: array(refSchema).optional()
|
|
7873
7879
|
}),
|
|
7874
7880
|
close: operation({
|
|
7875
7881
|
action: literal("close"),
|
|
@@ -9641,6 +9647,10 @@ var validPath = (value) => typeof value === "string" && value.length > 0 && ![..
|
|
|
9641
9647
|
}) && !value.split(/[\\/]/).includes("..");
|
|
9642
9648
|
var callerValue = (value) => ({ host: value.host, actor: value.actor });
|
|
9643
9649
|
var workerIdOf = (value) => value.workerId ?? null;
|
|
9650
|
+
var currentWriterOwnsTask = (task, workspace, caller) => {
|
|
9651
|
+
const owner = workspace.writer?.owner;
|
|
9652
|
+
return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
|
|
9653
|
+
};
|
|
9644
9654
|
function assertProductWriteAllowed(input) {
|
|
9645
9655
|
if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
|
|
9646
9656
|
return failure("invalid_input", "invalid product write path");
|
|
@@ -9998,9 +10008,11 @@ var standingReceiptFor = (root, workspace, cls, dir) => {
|
|
|
9998
10008
|
var verifyStandingApproval = (root, task, caller, binding) => {
|
|
9999
10009
|
if (task.status !== "active")
|
|
10000
10010
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
10001
|
-
const
|
|
10002
|
-
if (!
|
|
10003
|
-
return
|
|
10011
|
+
const workspace = new TaskStore(root).readWorkspace();
|
|
10012
|
+
if (!workspace.ok)
|
|
10013
|
+
return workspace;
|
|
10014
|
+
if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
|
|
10015
|
+
return failure("permission_denied", "standing approval requires the current writer session");
|
|
10004
10016
|
const standing = binding.standing;
|
|
10005
10017
|
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
10006
10018
|
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
@@ -10237,6 +10249,20 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
10237
10249
|
};
|
|
10238
10250
|
var verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding);
|
|
10239
10251
|
var storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok);
|
|
10252
|
+
var approvalAuthorityMatches = (store, task, approval, current) => {
|
|
10253
|
+
if (approval.kind !== "host_observed" || approval.receipts.length === 0)
|
|
10254
|
+
return false;
|
|
10255
|
+
if (provenanceMatches(approval, current))
|
|
10256
|
+
return true;
|
|
10257
|
+
const session = current.kind === "host_observed" ? current.session : null;
|
|
10258
|
+
if (!session || session.kind !== "host")
|
|
10259
|
+
return false;
|
|
10260
|
+
const workspace = store.readWorkspace();
|
|
10261
|
+
return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
|
|
10262
|
+
host: session.host,
|
|
10263
|
+
actor: session.handle
|
|
10264
|
+
});
|
|
10265
|
+
};
|
|
10240
10266
|
var validateAction = (store, task, workspaceId, input, authority) => {
|
|
10241
10267
|
if (task.status !== "active")
|
|
10242
10268
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
@@ -10262,7 +10288,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
10262
10288
|
return failure("permission_denied", "native reservation authority does not match decision");
|
|
10263
10289
|
if (decision.response !== "approved")
|
|
10264
10290
|
return failure("permission_denied", "decision was rejected");
|
|
10265
|
-
if (
|
|
10291
|
+
if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
|
|
10266
10292
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
10267
10293
|
const receipts = entry.provenance.receipts;
|
|
10268
10294
|
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
@@ -10429,7 +10455,7 @@ function settleAction(input) {
|
|
|
10429
10455
|
return failure("not_found", "decision not found");
|
|
10430
10456
|
if (!authorityDecisionMatches(authority, entry.data))
|
|
10431
10457
|
return failure("permission_denied", "native settlement authority does not match decision");
|
|
10432
|
-
if (!
|
|
10458
|
+
if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
10433
10459
|
return failure("permission_denied", "native settlement caller does not match approval");
|
|
10434
10460
|
if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
|
|
10435
10461
|
return failure("permission_denied", "decision binding is invalid");
|
|
@@ -10528,7 +10554,7 @@ function reconcileAction(input) {
|
|
|
10528
10554
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
10529
10555
|
if (!entry || entry.data.purpose !== "action")
|
|
10530
10556
|
return failure("not_found", "decision not found");
|
|
10531
|
-
if (!authorityDecisionMatches(authority, entry.data) || !
|
|
10557
|
+
if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
10532
10558
|
return failure("permission_denied", "native reconciliation caller does not match approval");
|
|
10533
10559
|
if (entry.data.response !== "approved" || entry.data.revoked !== null || entry.data.digest !== decisionDigest(entry.data) || entry.data.binding.taskId !== current.id || entry.data.binding.workspaceId !== current.workspaceId || entry.data.binding.workspaceId !== workspace.data.id)
|
|
10534
10560
|
return failure("permission_denied", "reconciliation decision binding is invalid");
|
|
@@ -10865,7 +10891,7 @@ var resolveBinding = (evidence, pin, currentCandidateId) => {
|
|
|
10865
10891
|
candidateId: evidence.candidateId ?? fallback
|
|
10866
10892
|
};
|
|
10867
10893
|
};
|
|
10868
|
-
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId
|
|
10894
|
+
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null);
|
|
10869
10895
|
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")));
|
|
10870
10896
|
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 }));
|
|
10871
10897
|
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
@@ -11271,6 +11297,11 @@ var exportDigest = (bundle) => sha256({
|
|
|
11271
11297
|
task: bundle.task
|
|
11272
11298
|
});
|
|
11273
11299
|
// packages/workit-core/src/core/external-action.ts
|
|
11300
|
+
var chainStepSchema = union([
|
|
11301
|
+
string2().min(1),
|
|
11302
|
+
object({ branch: string2().min(1) }).strict(),
|
|
11303
|
+
object({ pr: literal(true) }).strict()
|
|
11304
|
+
]);
|
|
11274
11305
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
11275
11306
|
object({
|
|
11276
11307
|
operation: literal("git.branch_setup"),
|
|
@@ -11285,7 +11316,7 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
11285
11316
|
operation: literal("git.commit"),
|
|
11286
11317
|
payload: object({
|
|
11287
11318
|
message: string2().min(1).optional(),
|
|
11288
|
-
plan_steps: array(
|
|
11319
|
+
plan_steps: array(chainStepSchema).min(1).max(32).optional(),
|
|
11289
11320
|
plan_branch: string2().min(1).optional()
|
|
11290
11321
|
}).strict()
|
|
11291
11322
|
}).strict(),
|
|
@@ -11771,6 +11802,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
|
|
|
11771
11802
|
expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
|
|
11772
11803
|
state: expected.state,
|
|
11773
11804
|
session: expected.session,
|
|
11805
|
+
report: expected.report ?? null,
|
|
11774
11806
|
provenance: result.data,
|
|
11775
11807
|
...binding
|
|
11776
11808
|
});
|
|
@@ -11781,7 +11813,7 @@ var takeWorker = (token, binding, expected) => {
|
|
|
11781
11813
|
verifiedWorkers.delete(token);
|
|
11782
11814
|
if (!value)
|
|
11783
11815
|
return null;
|
|
11784
|
-
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session))
|
|
11816
|
+
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session) || !sameValue(value.report, expected.report ?? null))
|
|
11785
11817
|
return null;
|
|
11786
11818
|
return value;
|
|
11787
11819
|
};
|
|
@@ -11822,6 +11854,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11822
11854
|
return failure("invalid_transition", "worker already has an observed session");
|
|
11823
11855
|
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
11824
11856
|
return failure("invalid_transition", "worker launch was not claimed");
|
|
11857
|
+
if (input.report && input.state !== "stopped")
|
|
11858
|
+
return failure("invalid_input", "worker reports require a stopped worker");
|
|
11825
11859
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
11826
11860
|
return failure("invalid_input", "running worker observations require a session");
|
|
11827
11861
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -11838,12 +11872,13 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11838
11872
|
data: {
|
|
11839
11873
|
...entry.data,
|
|
11840
11874
|
state: terminalCancel ? "cancelling" : input.state,
|
|
11841
|
-
session: input.session
|
|
11875
|
+
session: input.session,
|
|
11876
|
+
report: entry.data.report ?? input.report ?? null
|
|
11842
11877
|
}
|
|
11843
11878
|
};
|
|
11844
11879
|
const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
11845
11880
|
const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
11846
|
-
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
|
|
11881
|
+
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
|
|
11847
11882
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
11848
11883
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
11849
11884
|
taskId: task.data.id,
|
|
@@ -12261,14 +12296,30 @@ class WorkitCore {
|
|
|
12261
12296
|
const listed = this.store.listTasks();
|
|
12262
12297
|
if (!listed.ok)
|
|
12263
12298
|
return listed;
|
|
12264
|
-
const
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12271
|
-
|
|
12299
|
+
const status = input.status ?? "open";
|
|
12300
|
+
const limit = input.limit ?? 20;
|
|
12301
|
+
const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
|
|
12302
|
+
if (selected.length === 0)
|
|
12303
|
+
return success(null, null, []);
|
|
12304
|
+
const workspace = this.store.readWorkspace();
|
|
12305
|
+
if (!workspace.ok)
|
|
12306
|
+
return workspace;
|
|
12307
|
+
if (!workspace.data)
|
|
12308
|
+
return failure("not_found", "workspace not found");
|
|
12309
|
+
const tasks = selected.map((task) => ({
|
|
12310
|
+
id: task.id,
|
|
12311
|
+
revision: task.revision,
|
|
12312
|
+
workspaceRevision: workspace.data.revision,
|
|
12313
|
+
createdAt: task.createdAt,
|
|
12314
|
+
updatedAt: task.updatedAt,
|
|
12315
|
+
runtime: task.runtime ?? null,
|
|
12316
|
+
objective: task.intent.data.objective,
|
|
12317
|
+
status: task.status,
|
|
12318
|
+
closure: task.closure,
|
|
12319
|
+
progress: task.progress,
|
|
12320
|
+
writer: task.status === "closed" ? null : workspace.data.writer
|
|
12321
|
+
}));
|
|
12322
|
+
return success(null, null, tasks);
|
|
12272
12323
|
}
|
|
12273
12324
|
case "inspect": {
|
|
12274
12325
|
const task = this.store.readTask(input.taskId);
|
|
@@ -12277,7 +12328,7 @@ class WorkitCore {
|
|
|
12277
12328
|
const helper = this.helperTaskGuard(task.data, true);
|
|
12278
12329
|
if (!helper.ok)
|
|
12279
12330
|
return helper;
|
|
12280
|
-
if (input.view === "summary")
|
|
12331
|
+
if ((input.view ?? "summary") === "summary")
|
|
12281
12332
|
return this.summary(task.data);
|
|
12282
12333
|
return this.view(task.data);
|
|
12283
12334
|
}
|
|
@@ -12621,7 +12672,7 @@ class WorkitCore {
|
|
|
12621
12672
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
12622
12673
|
const verified = evidence.some((entry) => {
|
|
12623
12674
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
12624
|
-
return findingVerificationPasses(finding.data.candidateId, {
|
|
12675
|
+
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
12625
12676
|
kind: entry.data.kind,
|
|
12626
12677
|
candidateId: entry.data.candidateId,
|
|
12627
12678
|
status: evaluation?.status ?? "missing"
|
|
@@ -12701,7 +12752,18 @@ class WorkitCore {
|
|
|
12701
12752
|
if (task.data.status !== "active")
|
|
12702
12753
|
return failure("invalid_transition", "paused or closed tasks cannot assign workers");
|
|
12703
12754
|
if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
|
|
12704
|
-
return failure("permission_denied", "worker assignment is outside task scope"
|
|
12755
|
+
return failure("permission_denied", "worker assignment is outside task scope", {
|
|
12756
|
+
fields: [
|
|
12757
|
+
{
|
|
12758
|
+
path: "assignment.scope",
|
|
12759
|
+
reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
12760
|
+
},
|
|
12761
|
+
{
|
|
12762
|
+
path: "task.intent.scope",
|
|
12763
|
+
reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
12764
|
+
}
|
|
12765
|
+
]
|
|
12766
|
+
});
|
|
12705
12767
|
if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
|
|
12706
12768
|
return failure("invalid_input", "worker assignment references an unknown decision");
|
|
12707
12769
|
if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
|
|
@@ -12908,7 +12970,15 @@ class WorkitCore {
|
|
|
12908
12970
|
...item,
|
|
12909
12971
|
recordedAt: mutation.now,
|
|
12910
12972
|
provenance: attested.data,
|
|
12911
|
-
data: {
|
|
12973
|
+
data: {
|
|
12974
|
+
...item.data,
|
|
12975
|
+
state: "dispatching",
|
|
12976
|
+
coordinator: {
|
|
12977
|
+
kind: "host",
|
|
12978
|
+
host: this.context.caller.host,
|
|
12979
|
+
handle: this.context.caller.actor
|
|
12980
|
+
}
|
|
12981
|
+
}
|
|
12912
12982
|
} : item)
|
|
12913
12983
|
});
|
|
12914
12984
|
}
|
|
@@ -12984,6 +13054,7 @@ class WorkitCore {
|
|
|
12984
13054
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12985
13055
|
state: "stopped",
|
|
12986
13056
|
session: null,
|
|
13057
|
+
report: null,
|
|
12987
13058
|
provenance: attested.data,
|
|
12988
13059
|
owner: this.authorityOwner,
|
|
12989
13060
|
store: this.store,
|
|
@@ -13291,10 +13362,12 @@ class WorkitCore {
|
|
|
13291
13362
|
return workspace;
|
|
13292
13363
|
if (!workspace.data)
|
|
13293
13364
|
return failure("not_found", "workspace not found");
|
|
13294
|
-
const
|
|
13365
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
13366
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
13295
13367
|
if (!current.ok)
|
|
13296
13368
|
return current;
|
|
13297
|
-
const
|
|
13369
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
13370
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
13298
13371
|
return success(task.revision, workspace.data.revision, {
|
|
13299
13372
|
id: task.id,
|
|
13300
13373
|
revision: task.revision,
|
|
@@ -13308,7 +13381,7 @@ class WorkitCore {
|
|
|
13308
13381
|
progress: task.progress,
|
|
13309
13382
|
policy: task.policy,
|
|
13310
13383
|
requirements,
|
|
13311
|
-
writer:
|
|
13384
|
+
writer: evaluationWorkspace.writer
|
|
13312
13385
|
});
|
|
13313
13386
|
}
|
|
13314
13387
|
view(task) {
|
|
@@ -13317,13 +13390,15 @@ class WorkitCore {
|
|
|
13317
13390
|
return workspace;
|
|
13318
13391
|
if (!workspace.data)
|
|
13319
13392
|
return failure("not_found", "workspace not found");
|
|
13320
|
-
const
|
|
13393
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
13394
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
13321
13395
|
if (!current.ok)
|
|
13322
13396
|
return current;
|
|
13323
|
-
const
|
|
13397
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
13398
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
13324
13399
|
return success(task.revision, workspace.data.revision, {
|
|
13325
13400
|
task,
|
|
13326
|
-
workspace:
|
|
13401
|
+
workspace: evaluationWorkspace,
|
|
13327
13402
|
capabilities: this.context.capabilities,
|
|
13328
13403
|
requirements,
|
|
13329
13404
|
evidence: evaluateEvidence(task, current.data)
|
|
@@ -13414,8 +13489,6 @@ class WorkitCore {
|
|
|
13414
13489
|
return helper;
|
|
13415
13490
|
if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
|
|
13416
13491
|
return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
|
|
13417
|
-
if (status === "active" && input.authorityRefs.length === 0)
|
|
13418
|
-
return failure("permission_denied", "resuming a task requires authority references");
|
|
13419
13492
|
const workspace = this.store.readWorkspace();
|
|
13420
13493
|
if (!workspace.ok)
|
|
13421
13494
|
return workspace;
|
|
@@ -13445,7 +13518,7 @@ class WorkitCore {
|
|
|
13445
13518
|
return failure("needs_input", "imported task requires fresh destination evidence");
|
|
13446
13519
|
if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
|
|
13447
13520
|
return failure("recovery_required", "imported task requires resume reconciliation");
|
|
13448
|
-
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
|
|
13521
|
+
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
|
|
13449
13522
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
13450
13523
|
resumeCandidate = current.data.candidate;
|
|
13451
13524
|
}
|
|
@@ -13575,7 +13648,7 @@ class WorkitCore {
|
|
|
13575
13648
|
task: (value, context) => success(context.revision, null, {
|
|
13576
13649
|
...value,
|
|
13577
13650
|
status: "closed",
|
|
13578
|
-
candidates:
|
|
13651
|
+
candidates: taskForView.candidates,
|
|
13579
13652
|
closure: {
|
|
13580
13653
|
outcome: closure.data.outcome,
|
|
13581
13654
|
at: context.now,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-cursor",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit Cursor plugin — shared MCP transport, native hooks, and fourteen method skills",
|
|
6
6
|
"keywords": [
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"build": "bun scripts/build.ts"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@brainervirus/workit-core": "^1.2.
|
|
48
|
-
"@brainervirus/workit-mcp": "^1.2.
|
|
47
|
+
"@brainervirus/workit-core": "^1.2.1",
|
|
48
|
+
"@brainervirus/workit-mcp": "^1.2.1"
|
|
49
49
|
},
|
|
50
50
|
"engines": {
|
|
51
51
|
"node": ">=24"
|